DevOps 指南位于https://12factor.net/config建议将网站机密(数据库密码、API 密钥等)放入环境变量中。与使用从版本控制中忽略的文本文件(JSON、XML、YAML、INI 或类似文件)相比,这样做有什么优势?
我发现复制带有机密的配置文件比处理 .bash_profile 和 Web 服务器配置中的环境变量要容易得多。我遗漏了什么吗?
答案1
作者列出了他们的理由,尽管有些不连贯。他们的主要论点是,很容易意外地签入配置文件,并且配置文件的格式各不相同,可能分散在系统各处(这三个理由对于与安全相关的配置(如身份验证令牌和凭据)来说充其量只是平庸的论点)。
根据我自己的经验,您基本上有以下三个选择,各有其优点和缺点:
将数据存储在配置文件中。
采用这种方法时,最好将它们与存储库本身隔离,并确保它们位于应用程序存储其内容的区域之外。
优点:
- 非常容易隔离和控制访问,特别是如果您使用 SELinux 或 AppArmor 之类的东西来提高整体系统安全性。
- 对于非技术用户来说通常很容易更改(这对于已发布的软件来说是一个优势,但对于特定于您组织的软件来说不一定是优势)。
- 易于管理大型服务器组。有各种配置部署工具。
- 相当容易地验证正在使用的确切配置是什么。
- 对于编写良好的应用程序,您通常可以通过更新配置文件然后向应用程序发送特定信号(通常是 SIGHUP)来更改配置而不中断服务。
缺点:
- 需要适当的规划来保证数据的安全。
- 您可能需要学习不同的格式(尽管现在只有少数格式需要担心,而且它们通常具有相似的语法)。
- 确切的存储位置可能在应用程序中进行硬编码,从而可能给部署带来问题。
- 解析配置文件可能会有问题。
将数据存储在环境变量中。
通常这是通过从启动脚本中获取环境变量和值的列表来完成的,但在某些情况下,它可能只是在程序名称之前的命令行中声明它们。
优点:
- 与解析配置文件相比,在几乎任何编程语言中,从环境变量中提取值都是很简单的。
- 您不必担心意外发布配置。
- 通过模糊性你可以获得一定程度的安全性,因为这种做法并不常见,而且大多数破解你应用程序的人不会立即想到查看环境变量。
- 应用程序本身可以控制访问(当它产生子进程时,它可以轻松地清理环境以删除敏感信息)。
缺点
- 在大多数 UNIX 系统上,访问进程的环境变量相当容易。某些系统提供了缓解此问题的方法(例如 Linux 上的
hidepid
mount 选项/proc
),但默认情况下这些方法并未启用,并且无法防范拥有该进程的用户的攻击。 - 如果您正确处理上述安全问题,那么查看某些东西正在使用的确切设置并不是一件容易的事。
- 您必须信任应用程序在产生子进程时清理环境,否则它会泄露信息。
- 如果不完全重启应用程序,您就无法轻松更改配置。
使用命令行参数传递数据。
说真的,无论如何都要避免这种情况,它不安全而且维护起来很麻烦。
优点:
- 比大多数语言中的环境变量解析起来甚至更简单。
- 子进程不会自动继承数据。
- 提供一种在开发应用程序时快速测试特定配置的简便方法。
缺点:
- 就像环境变量一样,在大多数系统上读取另一个进程的命令行很容易。
- 更新配置极其繁琐。
- 对配置的长度设置一个硬性限制(有时低至 1024 个字符)。
答案2
环境变量将被 Web 服务器的每个子进程继承。这些子进程包括连接到服务器的每个会话以及由它们生成的每个程序。这些秘密将自动向所有这些进程透露。
如果您将机密保存在文本文件中,则服务器进程必须能够读取它们,因此每个子进程也有可能能够读取它们。但至少程序必须去找到它们;它们不会自动提供。您还可以让一些子进程在不同的帐户下运行,并使机密只能由这些帐户读取。例如,执行指令在 Apache 中执行此操作。
答案3
即使在环境变量或文件方面需要做出一些与安全相关的权衡,我也不认为安全是此建议的主要驱动力。请记住,12factor.net 的作者也是(或曾经是?)Heroku PaaS 的开发人员。让每个人都使用环境变量可能大大简化了他们的开发。不同配置文件格式和位置种类繁多,他们很难支持所有这些文件。相比之下,环境变量很容易。
不需要太多的想象力就可以猜出一些曾经发生过的对话。
开发人员 A:“啊,这个秘密配置文件 UI 太混乱了!我们真的需要一个在 json、xml 和 csv 之间切换的下拉菜单吗?”
开发人员 B:“哦,如果每个人都使用环境变量来配置应用程序,生活将会变得如此美好。”
开发人员 A:“实际上,这样做是出于一些合理的安全相关原因。环境变量可能不会被意外地签入源代码管理。”
开发人员 B:“您不通过启动守护进程的脚本或者配置文件来设置环境变量吗?”
开发人员 A:“不是在 Heroku!我们会让他们将其输入到 UI 中。”
开发人员 B:“哦,看,我的 12factor.net 域名警报刚刚响了。” 1
1:来源:编造。
答案4
就我个人而言,我不建议设置环境变量,因为.bashrc
这些变量对全部由 shell 启动的进程,但将它们设置在守护进程/主管级别(init/rc 脚本、systemd 配置),以便它们的范围限制在需要的地方。
当独立团队管理运营时,环境变量为运营提供了一个简单的界面,可以设置应用程序的环境,而无需了解配置文件/格式和/或诉诸于内容的混乱。在多语言/多框架设置中尤其如此,运营团队可以根据运营需求(部署的简易性、可扩展性、安全性等)选择部署系统(操作系统、主管流程)。
另一个考虑因素是 CI/CD 管道 - 当代码经过不同的环境(即开发、测试/质量保证、准备、生产)环境细节(部署区域、数据库连接细节、凭证、IP 地址、域名等)最好由专用配置管理工具/框架设置,并由环境中的应用程序进程使用(以 DRY、一次编写、随处运行的方式)。传统上,当开发人员倾向于管理这些操作问题时,他们倾向于签入代码之外的配置文件或模板 - 然后在操作需求发生变化时最终添加解决方法和其他复杂性(例如,出现新的环境/部署/站点,可扩展性/安全性加重,多个功能分支 - 突然有手动部署脚本来管理/破坏许多配置文件) - 这种复杂性是一种干扰和开销,最好通过专用工具在代码之外进行管理。
- Env-vars 简化了大规模的配置/复杂性。
- Env-vars 将操作配置直接交给负责非代码以统一的(如果不是标准的)非约束性的方式对申请的相关方面进行规定。
- Env-vars 支持更换支持应用程序的主/监控进程(例如 god、monit、supervisord、sysvinit、systemd 等),当然,随着操作要求的发展/变化,还可以更换部署系统(操作系统、容器映像等)等。虽然如今每种语言框架都有某种进程运行时,但这些进程运行时往往操作性较差,更适合开发环境和/或增加多语言/多框架生产环境的复杂性。
对于生产环境,我倾向于在环境文件例如/etc/default/myapplication.conf
由配置管理部署并设置为仅可由root
以下人员读取的程序systemd
(或其他任何程序)可以在专用的特权系统用户在一个私人群组. 拥有专门的用户组支持ops
-sudo
默认情况下,这些文件在全世界范围内都是不可读的。它符合 12factor 标准,支持 Dev+Ops 的所有优点,并且具有良好安全性的所有优点,同时仍允许开发人员/测试人员在 dev/qa/test 环境中放入他们自己的 EnvironmentFiles。