查看原文
其他

如何消除“技术债”?高效DevOps团队的6个核武器

陈峻编译 51CTO技术栈 2018-08-05

如今,以 Netflix、Etsy、Flickr 为代表的各个公司,都持续以更少的时间、更快地发布出新的功能,而他们的成功秘诀就是用到了“DevOps”。


DevOps 如今已广泛地被银行、保险公司、政府和许多重监管的行业组织所采用。


这是一种交付软件的新方法,它专注于持续集成、持续交付和消除工程与运营团队之间的障碍,从而加快发布速度、并降低风险。


虽然 DevOps 被大多数软件团队所熟知,但是很少有团队真正实践并维护着与之相称的安全控制。


像 Uber 和 eBay 之类存储着大量个人敏感信息的组织,虽然运用 DevOps 简化了开发和运营,推进了功能开发的速度,但由于将安全隐患置于了次位,因此也遭遇过一些重大的违规事件。


当然,也有不少的公司在运用 DevOps“快速前行”的同时,保持着高标准的信息安全。

对此我们总结出了如上图所示的 6 条基本原则:

  • 安全策略即代码

  • 职责分离

  • 专注工作流与速度

  • 安全的首要位置

  • 自动化

  • 技术采用

安全策略即代码


目前,在那些成功地将 DevOps 的速度与安全性结合的团队中,最重要的特性是:他们运用代码来指定各种安全策略。


DevOps 的基石是“基础架构即代码(infrastructure as code)”,也称“不可变基础架构”。


运营者运用代码来声明其基础架构的需求,从而取代了手工管理与配置服务器与软件的旧模式。


这种方法能够有效地抑制那些已被手动配置的服务器“独角兽(unicorn)”们进一步扩散,进而造成在出现故障时,却无人知晓如何进行重建这一尴尬局面。


更重要的是:它还能支持自动化扩容,以及预测如何将新生成的功能部署到不同的测试与生产环境之中。


代表这种基础架构的代码会与应用程序代码一样被检入到源代码的控制之中,进而可以实现版本控制、比较和保护。


那些安全实践经验较为丰富的 DevOps 团队,可以通过采用“基础架构即代码”的模型来管理应用程序的安全性。他们在新的应用或微服务中,用代码来声明安全策略的各种需求。


例如,下面的一小段安全政策,就是一个用 Conjur 政策语言(请参考https://www.conjur.org/reference/policy.html)编写的、提供货币换算服务的微服务:

由上可见,它的语法是人类可读且易于理解的。即:该代码允许授予了货币转换服务(currency-converter)权限,去访问一个存储着各种货币值的数据库密码。


该政策是完全独立且可不限重复的,既不依赖于外部因素,也不会被未来的解释方式所左右。它完全可以被检入到源代码的控制中,作为应用代码的一部分被予以内部实现。


那些日常管理着数千个应用相关权限的安全人员,可以通过该“安全策略即代码”模型,将自身的团队能力提升到一个新的水平,并能使得自己的工作更具可预测性。而这些恰是过去那些需要手动配置与应用相关的安全权限所无法企及的。


所以,“安全策略即代码”对于创建高效的安全 DevOps 流程是至关重要的。没有它,我们后面将要谈到的安全 DevOps 各个方面都将无法实现。

职责分离


令人遗憾的是,在许多软件团队中,开发人员和操作人员还肩负着安全的职责,他们需要维护服务相关的帐户、定义各种安全控制、控制对于敏感数据的访问等。这些无疑增加了他们已有的且“满满的”工作量。


职责分离是速度的动因


大多数关于职责分离的文章都会提到它在防止利益冲突和欺诈方面的好处,以及如何通过它来减少任何人超额拥有其本该拥有的基本权力。


然而从另一个角度来看,高绩效的 DevOps 团队会将职责分离视为一种机会,他们籍此来保证团队成员仅专注于自己最擅长的工作,并能够根据每个人的能力来适当优化团队整体的工作流程。


成熟的团队具有明确的角色和清晰的职责分工。安全和监督由专门的安全人员来负责,开发人员会专注于编写代码,而操作人员则确保生产环境架构能健康运营。


每个职能团队之间的接口“传递”已被编入到了安全策略之中。即:开发人员创建安全策略声明其应用程序或服务所需要的权限→安全人员随即审核并批准相关的代码更改→操作人员确保应用程序的部署、并能够按照预期运行。


职责结合:一种痛苦的反模式


当任何一种开发、安全和运营角色被组合在一起时,组织将不可避免地出现由单一角色所带来的巨大风险。


对于开发者来说,他们通常无法意识到自己在生产系统中可能所拥有的权限,因此也就无法预知自己的某种疏忽会给组织带来的危害程度。


例如:去年某公司的初级工程师在入职的第一天,就意外地删除了该公司的生产数据库。而发生这种情况的原因,正是因为他被赋予了在生产系统中的所有权限。


就他所拥有的访问权限而言,这位不幸的开发人员在完全不知的情况下,同时扮演着运营人员的角色。


因此,问题的根源不在他的身上,而来自雇主:他们没有将职责分离,没能在源头防范此类情况的发生。


每种角色的责任


开发人员:

  • 能够编写代码和测试案例,而不必担心意外地触碰生产环境。

  • 与运营人员交流其应用所涉及到的主机需求。

  • 与安全人员交流其应用所涉及到的安全需求。


运营人员:

  • 执行并实现应用程序有关主机(虚机)的需求。

  • 保证生产环境的可用性。


安全人员:

  • 针对开发人员的应用需求授予相关权限。

  • 确保系统范围内,各种安全措施的落实与到位。


正如企业采用 DevOps 模式并不意味着开发人员需要承担运营角色那样,采用“DevSecOps”也并非意味着开发人员必须成为安全专家。


因此,高绩效的组织应当学会避免出现“快速推进却时常犯错”的行动状态。他们需要的应当是“在知晓不会犯错的基础上快速推进”的各司其职模式。

专注工作流与速度


DevOps 的核心概念是产生可持续的、平稳的工作流程。


为了实现此目标,DevOps 团队需要通过创建各种 CI/CD 管道、持续进行小而频繁的代码变更、端到端的部署、并采用类似基于微服务的系统架构,以获取上文提到的快速推进。


建模和优化安全的工作流程


看板(Kanban)是一个专注于流程和开发速度的工作系统。其核心是将工作的各个阶段可视化,即:随着项目在不同阶段的推进,各个状态的可视化会有助于分解出其中出现的缓慢步骤、识别出瓶颈、并优化其速度。


一个长期面临发布延迟的组织可能会发现:安全审查阶段在自己的系统中花费的时间较长。


原因通常在于:安全审查开始得比较晚,并且所涉及的量又比较大。换句话说:太多的东西会一下子被“甩到”安全审查团队的面前。


那些经验丰富的组织一般会“将安全放到左边”(在可视化看板中,一般假设工作流程从左边开始,向右边流动)。


这就意味着:安全团队越早介入新版本/功能的开发,就越好。同时,如果他们在整个开发周期的早期阶段,就能揭示出重大的安全问题,那么就可以防止后期出现延迟交付的情况。


为速度加固微服务


微服务架构给安全团队提供了许多好处,尤其是那些专注于自身发布速度的团队。


我们继续以前面 “货币换算”的微服务为例。如果安全团队只需要检查并授权该应用去访问单个数据库的话,那么还是相对较为简单的。


但是如果该功能被置于一个庞大的且拥有数百万行代码的应用体系之内时,那么安全团队就可能需要对许多方面的变化进行梳理,以查找出对他们来说非常重要的审查内容。


同时,如果该庞大的应用在版本上经历了许多实质性的变更,则会将审查变得更困难。


毕竟,只让安全团队检查几十行的安全策略代码和让他们检查上千行代码相比还是容易得多的。


如果新的版本不需要修改任何权限,那么对于已经注册的微服务代码进行持续升级还是能保持快速的。


相反如果需要在庞大的应用中退役或关停某个微服务,则需要考虑到所涉及的各种耦合关系和代码层面的规模。


传统团队在经历了瀑布式开发之后,常趋向于进行“爆炸式(big-bang)”发布应用。而一旦应用出现了质量问题,则不得不迅速叫来安全人员进行代码审查。


如此一来,安全团队所面临的往往是累积了成上千次变更后的应用代码和来自业务方面为了应用能早日上线的不断施压。


因此,那些保持持续交付的大型团队,会将他们的代码库分解成多个微服务来实现。


同时他们也会邀请安全团队运用各种专业的、精细化的模型来进行各种安全审查,以有助于加快发布进度、并提高可视性。


端到端简化流程


DevOps 引入了“尽早实现端到端”的概念。这是一种简单的降低交付风险、并提高预估信心的方法。


对于团队成员来说,他们能够越快地将新服务“连接管道”,就能够越快地获知如何顺利地去构建部署流程,以及应对频繁的升级和更新。


同时,一些未完全实现的功能标志可以被隐藏地部署到生产环境之中,等到新的功能完善了,再向用户全面展示和开放。


这种方法同样也能够很好地降低服务中的安全变更风险,特别是当团队采用了“安全策略即代码”的原则后。


例如,一项新的服务需要访问内部 CRM 数据库和支付网关,那么该团队的首要任务就是为该服务构建出一个 CI/CD 管道,然后等基本连接构建好之后,再将该管道升至 0.1 版本。


如此,该服务的权限就已经通过了审查,一旦完成部署,它将以“预先注册”的方式去访问生产环境中的各种敏感资源。


由此可见,这样便消除了后续版本的部署风险,开发人员可以在后期去创建并实现剩余的功能。同时,安全团队也提前获知了服务的权限与安全的设置需求。


高效的 DevOps 团队应当能够通过如下方式,来实现高速且流畅的工作流程:

  • 使用看板视图来发现开发周期中的瓶颈,并优化它们。

  • “将安全放到左边”,让安全团队尽早参与到开发周期之中,从而尽早发现相关的安全问题。

  • 使用“安全策略即代码”的方法让开发、安全和运营团队进行高效且明确的沟通。

  • 将大型应用分解为更小的微服务,每个微服务都要有自己的安全策略。

  • 尽可能减少大批量的变更,使安全团队能够持续、少量、可预知地进行安全审查。

  • 尽早实现“端到端”模式,以降低组件和模块的持续升级所带来的风险。


无论您的公司有着上百位开发人员,还是只有较小规模的团队,上述安全开发的流程原则都会给您带来帮助。

安全的首要位置


软件团队通常会对严重的错误、蹩脚的设计和糟糕系统性能做出及时的响应。因为这些问题既明显又尖锐。


而作为“硬币另一面”,他们对于那些所谓的“技术债”,则只是通过一些预防性工作予以滞后解决,甚至挤压下来,直到将来失控,并造成更大的问题。


因此,经验丰富的 DevOps 团队通常应当把安全问题视为“头等公民”,放在急需解决的首要位置,而不能将它们积压到 To-Do(待办)列表中,甚至永远不去解决。


既然一个团队能够重视应用中的漏洞以及安全性,那么他们自然会在开发和发布过程中采取各种恰当的安全措施。我们下面来详细研究一下这些安全实践。


密码的安全管理


密码的安全管理需要注意以下几点:

  • 与生产系统有关的所有密码信息(密码、私钥、或攻击者可以利用的任何敏感信息)都应存储在安全且高可用的库中,并且只有被授权访问它们的系统,能在恰当的时间段访问到。

  • 不允许库管访问到具体的密码值/信息。

  • 确保密码根据既定的时间表自动变更,进而控制其有效时间。


良好的安全习惯


下面是以“安全为中心”的团队时常践行的一些安全要点:

  • 最小权限的原则可以适用到任何地方。机器也好、人员也罢,只能访问他们适合访问的资源。一旦有不恰当的访问发生,应立即撤销其对应的权限。

  • 漏洞扫描会自动运行在所有相关的第三方特征库之上。如果有最新的更新,则应立即升级各个漏洞库。

  • 无论是否检测到相关的漏洞,都应保持第三方特征库为最新。因为某些修补程序可能仅适用于特征库的最新版本,因此升级与更新将有助于避免出现重大的兼容性问题。

  • 针对潜在攻击因素,应定期对应用程序(及其 CI/CD 环境)进行渗透测试。这些测试有时甚至可以作为 CD 管道的一部分,被自动化工具来完成。当然也可以通过定期的人为干预,如白帽子黑客,来增强效果。

  • 将应用与信息安全方面的培训,放到所有新入职的开发人员的培训计划中,并让他们得到持续教育。

  • 为产品代码的变更开发工作流程,其中包含:攻击向量因素和安全策略违规等信息准确检查与披露。例如,某个团队使用着 GitHub PR(PullRequest),那么提交者和审阅者都应使用同一个 PR 模板来进行编写。


全员致力于安全速度


在一些公司里,安全团队常背负着“挡路人”的名声。而实际情况却是:在整个开发周期中,安全团队往往过迟地获悉新的系统与计划。


而无论开发进程是多么的流畅与快速,安全团队始终有责任确保整体业务,不会因为新技术的部署而面临风险。


安全审批是需要时间的,特别是有大量项目并发进行的时候,当然,各种不满的情绪很容易滋生。


在一些“失常”的组织中,开发人员甚至会秘密地部署那些未经批准的应用,以颠覆安全团队的权威,从而使事态更为恶化。


因此,高绩效的组织应当从上述问题中认识根源,即:安全团队被放置在了开发流程的错误位置。


将安全放到左边


经验丰富的团队往往在开发新应用的早期阶段就直接考虑到安全问题,让安全团队去检查他们的架构和技术选择,并努力在流程中“将安全放到左边”。


如此,在大量代码被编写之前,安全团队就能提早发现各种严重的问题,并能尽早解决。


同时,此举也会减少开发人员和安全人员之间的争论,塑造出和谐的团队关系。


乍看来,“将安全放到左边”在短时间内会是一项吃力不讨好的选择。但是如果从整个开发的生命周期来看,早期的安全评估势必会避免主要架构在后期出现漏洞时的返工成本。


实际上,早期的安全审查不但能降低开发项目的风险,还能为后续的发布周期降低成本。

自动化


对于 DevOps 团队来说,他们已经能够实现 QA、打补丁、部署、升级、回滚和灾难恢复的自动执行,并达到了前面提过的“不犯错式快速推进”的效果。


而对于高绩效的 DevOps 团队来说,他们还应当将安全性纳入现有的自动化规划之中,在不增加不当风险的前提下,实现快速的发布功能。


人类只做最擅长的事


经验丰富的 DevOps 团队不仅能够最大限度地发挥系统自动化,还会持续寻找那些将人类重复执行的任务转化为自动化的机会。


而安全性恰好就属于这种自动化转化的范畴。当然,在安全开发中也存在着一些仅适合人类完成的方面。


例如:在新的应用程序被推出之前,以及在管理现有的应用程序时,安全团队都是审查和判断授予各项权限的最佳选择。


尽管有许多高效的自动化工具可用于渗透测试,但是人类总能找到去攻击软件或系统的更好、更新的方法。


增加自动化,降低人数


在大多数高绩效的 DevOps 团队中,一旦他们将持续集成(CI)的管道设置为以自动化的方式将应用程序推送到下个阶段或是生产环境,那么操作人员就不必再参与应用程序的后期升级了。


同样,只要应用程序的安全政策没有发生变更,且不需要增加新的权限,那就不需要让安全人员作为审批的一个环节,也不可能造成瓶颈,甚至影响到应用的推进或上线。


他们既然已经人工批准了某个应用的注册,那么只要该应用程序在运行时不需要更多的权限去访问资源,升级应该被完全自动化。


当然,在不影响安全性的情况下,实现自动升级就需要在 CI 管道中设置更多的任务,包括:扫描第三方软件库和运行中的 CVE(Common Vulnerabilities and Exposures),检查应用程序的代码是否存在着安全漏洞,以及是否进行渗透测试等。


这些自动化的配置虽然可能会需要一定的时间,但当它们被相互累计起来时,其成本、风险以及整体所花费的时间,都会比人工进行要节省得多。


自动化安全


随着数据中心自愈能力和灾难恢复(DR)技术的发展,自动化安全沿着如下的发展路径,逐步缓解了运营中可能出现的各种复杂故障:

  • 维护一个需要手动启用的备用站点。

  • 对于手动站点的切换流程进行定期的演习与测试。

  • 完全自动化地切换到另一站点。

  • 多个站点或区域同时运行,在出现故障时,能自动在系统中同步所有的差异。

  • 通过运用混沌工程(Chaos Engineering)原理故意造成基础设施故障,进行测试。


自动化防范入侵


自动化不但能协助高效的安全团队做出及时的故障响应,还能防范各种入侵行为的发生。


众所周知,攻击者入侵系统是需要时间的。如果某个关键数据库的帐户密码需要每 90 天手动更改一次的话,那么他们将有几个月的时间去试图破解它。


但是,如果该数据库的密码每天都自动更改(轮询的方式),或更为频繁的话,那么他们破解密码的能力将大幅降低。


自动化响应入侵


另一个防范入侵的关键因素是:定期自动化重构基础设施。即:通过虚拟机技术和自动化进程,可以定期将服务器快速地恢复到已知的良好状态,而不产生任何的宕机时间。


在许多组织内,各种自动化响应流程在检测到入侵发生时,就启动了自动恢复的进程,所有密码被轮询一次,而所有相关的“可调用”组件(如虚拟机、容器等)也被重新创建到过去已知的良好状态。


上面我们提到过,应当对灾难恢复方案进行持续演习那样,如果自动化流程也能够持续测试与执行的话,那么其相应的风险也会大幅降低。

技术采用


技术发展得如此迅速,以至于安全人员常被开发人员贴上“过时策略的执行者”和“拒绝采用现代化工具”的标签。


如今,DevOps 使用到了各种 SaaS 产品,支持 CI、CD、配置管理以及各种编排的各种开源解决方案。


安全团队要想适应该环境,并流畅地开展工作,就必须拥抱这些新的系统与技术。


安全理念的变迁


劣等的安全团队会负面地看待新的技术。对他们而言,现代化的工具代表了不断增加的威胁因素、潜在的漏洞和受攻击的可能性。


而优等的安全团队则持有乐观的态度,即:如果能安全地采用新的工具,组织的业务不但能够运行得更快,而且组织也会更为安全。


传统的 IT 安全理念建立在静态的本地数据中心和已知资产明细的基础上。在系统边界处,外围防火墙作为主要的防御措施,抵御着各类攻击。


而内部安全系统则专注于管理组织里的人员与机器。LDAP、Kerberos 和 Active Directory 是主流的管理身份与角色的工具。新添置的机器和新雇用的员工需要被手动配置到系统之中。


如今,现代化的组织则使用微服务、容器、编排器(orchestrators)和无服务器技术来管理动态的基础架构。


显然这些与传统的安全理念并不相称。新的安全理念不再专注于“守住”生产环境中的资产,而是要更好地且“安全地支持企业的快速推进”。


新技术提高了安全性


新的安全保护措施,有助于各种安全流程跟上 IT 系统的发展节奏,并能防止影子 IT 的威胁。


例如,Beyond Corp 是 Google 的一个安全模型,它能够将各种内部应用转换为典型的部署模型。Google 并没有将那些敏感的应用放置在 VPN 后面,而是将它们部署到了公网之上。


对于这些应用的访问,完全取决于访问者的身份,而并非他们所处的网络。这就颠覆了传统的IT策略,并为 Google 和使用它的其他组织提供了巨大的速度与灵活性。


Conjur 同样在其产品中也用到了:将身份识别应用于所有的动态计算资产与用户的安全理念。


其他一些以开发人员为中心的技术,也从根本上减少了开发者所需要访问的敏感资产数量。它们有助于更好地实现职责分离。


例如,像 Pivotal Cloud Foundry 之类的 PaaS 系统和像 Kubernetes 这样的部署编排器,能为开发人员提供完全隔离的、且独立于系统操作的清晰视图。


无服务器技术同样能够大幅提高系统的安全性。由于使用虚拟机或容器的开发人员,能够“近距离地”接触到操作系统,这给资产和敏感数据带来了风险。


然而,在无服务器或“功能即服务(FaaS)”的模型中,这些资产是完全无法被访问到的。


因此,对于大多数安全团队来说,与其去监控并保护开发人员可能访问到的资产,不如直接不让他们“知晓”。


旧思维与新思想


传统的 IT 安全模型着眼于:确定哪些资产需要被控制,然后构建出结构来“守住”它们。因此,所有工作流程都基于这种集中式的管控模型。


而新的方法则假设:手头已经没有一套完整的系统资产对应图表了,应当使用的简单而有效的准则。


即:每个实体都必须具有身份,必须通过策略清楚地传达权限的更改,明确职责的分离,通过自动化的实现提高业务的推进速度。


借助这一全新的理念,现代化的 DevOps 团队将能够在不牺牲安全性的情况下,保持高速的交付能力。而且这种新的理念、方法和模型势必会被所有的组织所接受。


作者:Brian Kelly,陈峻编译

编辑:陶家龙、孙淑娟

投稿:有投稿、寻求报道意向技术人请联络 editor@51cto.com

精彩文章推荐:

阿里资深技术专家:谁说程序员是吃“青春饭”的?

兵败DevOps!一个Bug损失4.6亿美金,不得不看的惨痛教训!

服务器不丢包背后的兵法:Redis在万亿级日访问量下的中断优化

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存