服务器/数据库configuration文件,包括密码,应该存储在源代码pipe理中吗?

我期待听到一些最佳做法…

假设一个Web应用程序与几个不同的生产服务器(数据库等)交互…应该包含数据库密码的configuration文件存储在源代码pipe理(例如,git,svn)?

如果没有,跟踪应用程序需要访问的服务器数据库(或其他相关)密码的最佳方式是什么?

编辑:添加一个赏金,以鼓励更多的讨论,并听取更多的人认为最好的做法。

这里没有单一的“银弹”答案,这将大大取决于细节。

首先,我认为最佳实践是将所有源代码与独立存储库中的configuration分开。 所以,源代码仍然是源代码,但它的安装或部署(configuration,密码等)是另一回事。 通过这种方式,您可以将开发人员的任务与系统pipe理员的任务牢牢分离开来,最终可以build立两个不同的团队,做他们擅长的事情。

当你有独立的源代码库+部署库时,最好的下一个select是考虑部署选项。 我在这里看到的最佳方式是使用典型的选定操作系统的部署过程(即为操作系统维护者所做的操作,为选定的操作系统构build自治包)。

例如,红帽或Debian打包程序通常意味着从外部站点抓取软件包(即从源代码VCS导出源代码),将其解压缩,编译和准备准备部署的包。 部署本身应该理想的意思就是做一个快速简单的命令来安装软件包,比如rpm -U package.rpmdpkg --install package.deb或者apt-get dist-upgrade (假设你的软件包转到其中apt-get将能够find它们)。

显然,为了使它以这种方式工作,您必须为处于完全工作状态的系统的所有组件(包括所有地址和凭证)提供所有configuration文件。

为了得到更简洁,让我们考虑一个典型的“小型服务”的情况:一个PHP应用程序部署在运行Apache / mod_php的n个应用程序服务器上,访问m个 MySQL服务器。 所有这些服务器(或虚拟容器,实际上并不重要)都驻留在受保护的专用networking中。 为了让这个例子更容易,让我们假设所有的真正的互联网连接都是由一些k http加速器/反向代理(例如nginx / lighttpd / apache)组成的,这些configuration非常简单(只有内部IP可以转发)。

我们对他们有什么联系和充分的工作?

  • MySQL服务器:设置IP /主机名,设置数据库,提供login和密码
  • PHP应用程序:设置IP /主机名,创buildconfiguration文件,提到MySQL服务器的IP地址,login名,密码和数据库

请注意,这里有两种不同的“信息types”:IP /主机名是固定的,你可能想要一次性分配它们。 另一方面,login名和密码(甚至数据库名)纯粹是为了连接的目的 – 确保MySQL确实是我们的PHP应用程序连接到它。 所以,我的build议是将这两种“types”分开:

  • “永久”信息(如IP)应存储在某个VCS中(与源代码VCS不同)
  • “瞬态”信息(例如2个应用程序之间的密码)不应该被存储,而是在生成部署包期间生成。

最后也是最困难的问题仍然在这里:如何创build部署包? 有多种可用的技术,主要有两种方法:

  • 从VCS1导出的源代码+从VCS2 +的“永久”configuration从VCS3 =包生成脚本
  • 源代码在VCS1中; VCS2是一个分布式版本控制(如git或hg),它基本上包含VCS1 +configuration信息的“分支”和可以生成的构build脚本。 我个人比较喜欢这种方法,它更短,最终也更容易使用,但是学习曲线可能会更陡峭些,特别是对于那些需要掌握git或hg的pipe理员来说。

对于上面的例子,我会创build如下的包:

  • my-application-php – 这将取决于mod_php,阿帕奇,并将包括生成的文件,如/etc/my-php-application/config.inc.php将包括MySQL数据库IP /主机名和login名/密码生成为md5(current source code revision + salt) 。 该软件包将安装在n个应用程序服务器的每一个上。 理想情况下,它应该能够安装在干净安装的操作系统上,并且无需任何手动操作即可构build完整的应用程序集群节点。
  • my-application-mysql – 这将取决于MySQL服务器,并将包括后安装脚本:
    • 启动MySQL服务器,并确保它将在OS启动时自动启动
    • 连接到MySQL服务器
    • 检查是否需要数据库存在
    • 如果没有 – 创build数据库,引导它的内容,并创build密码login(相同的login名和密码在/etc/my-php-application/config.inc.php生成,使用md5algorithm)
    • 如果是 – 连接到数据库,应用迁移到新版本,杀死所有旧的login名/密码,并重新创build新的login名/密码对(同样,使用md5(修订+盐)方法生成)

最终,它应该带来使用单个命令(如generate-packages && ssh-all apt-get dist-upgrade部署的好处。 此外,您不需要在任何地方存储应用程序间密码,并且每次更新都会重新生成密码。

这个相当简单的例子说明了您可以在这里使用的很多方法 – 但是最终,由您来决定哪个解决scheme更好,哪个方法更有效。 如果你在这里提出更多的细节或作为一个单独的问题,我会很乐意尝试细节。

撇开密码永远不应该以纯文本的forms存储在任何地方 (除了某人的头盖骨或只有首席执行官,首席财务官和首席信息官才能访问的locking金库之外),应该将所有内容存储到源代码pipe理这是build立你的产品所需要的。

这意味着不仅仅是你的源代码,甚至包括编译器,编译器选项,编译器本身等等的规范。

如果我们能够find一种方法来检查物理硬件,我们也会这样做:-)

构build过程本身可以复制的任何东西,或者任何运行而不是构build软件(比如你的密码)的东西通常都不受源代码控制,但是一些商店会为它们的可执行文件,生成的文档等等这样他们可以很快得到一个特定的版本来安装。

密码不应该存储在源代码pipe理中。 完全一样。 永远。 请参阅如何保密秘密

密码,服务器名称等是由服务器pipe理员执行的部署configuration的一部分。 logging这个程序是非常重要的,并将文档化的程序置于控制之下。

或者,部署configuration可以通过sysadmin将运行以执行configuration的脚本来执行,并且在脚本执行期间,它会要求系统pipe理员提供所需的信息。 这个脚本也必须保存在版本控制中。

除了服务器configuration之外的其他一切都必须在源代码控制之中。

在源代码控制中存储服务器configuration通常是一个坏主意,因为它阻碍了部署,并可能导致小的灾难(例如,当有人没有意识到从源代码控制部署的testing版本正在与实时服务进行通信时)。

始终将这些configuration文件保存在Webroot之外。

受信任的连接可能是一个选项,允许已知的IP地址通过configuration该服务连接到服务。

  • 在Windows上运行时使用集成身份validation。 请参阅保护数据访问
  • MySQLconfiguration为允许来自本地主机的连接,而不需要密码。 请参阅步骤7:在Windows上保护MySQL服务器
  • PostgreSQL你可以使用〜/ .pgpass 。

一般来说,我同意paxdiablo:把所有你可能在源代码控制之下。 这包括带有数据库凭证的生产configuration文件。

想想你的服务器崩溃的情况,备份结果是坏的,你需要得到服务器的备份。 我认为你和你的客户(或老板)肯定会同意,在源代码pipe理中部署这个站点所需要的一切都是一大优势。

如果要使用持续集成(另一个最佳实践)从源代码构build可轻松部署的包,则必须将configuration文件置于源代码控制之下。

还要考虑到在大多数情况下,具有源代码pipe理权限的开发人员不能直接访问生产数据库服务器。 生产密码对他们无用。

如果错误的人员获得了访问源,他们仍然需要访问生产服务器,以便对密码造成伤害。 因此,如果您的生产环境得到了适当的保护,则源代码pipe理中密码的安全风险非常有限。

我认为这个问题更多的是关于信息所有权,信任和组织。 您应该问问自己,您信任哪个组织的哪个部分保证您的系统密码不被泄露和滥用?

我一直在负责这个行业的人所在的组织里。 在其他人中,他们已经被委托给运营团队,他们也拥有围绕创build和使用等的stream程。

最重要的是,你的组织应该明确定义谁应该有权访问系统密码。 之后,您可以决定适当的技术解决scheme来保护密码。

否。生产密码应该直接在服务器上configuration。 您应该为部署团队/人员创build部署说明,以便在部署期间更改正确的属性文件。

我发现使用构build脚本(在我的情况下是Phing)是input密码的最佳方法。

在我的Subversion Repos for PHP中,包含密码的configuration文件被检入为config.php.sample ,提示必须提供的内容,依赖的脚本需要config.php出现在相同的位置。

该存储库configuration为忽略该目录的config.php ,以避免“意外”添加或签入。

示例configuration文件,当然,我会把他们在版本控制。 但通常不会与真实世界的访问数据,如服务器地址或密码。 更多的东西

 #program.conf
 #
 $ myprog的#mysql选项。
 #
 #SERVER_ADDR = 127.0.0.1
 #SERVER_USER = mysql的
 #SERVER_PASSWD = ABCDEF

源代码中的密码问题:

  • 很难从一个部署到另一个(我不想在生产中修改源代码)
  • 发展中意外破坏生产数据库的可能性增加
  • 安全问题(在大多数商店中,代码/开发者没有理由知道产品密码)
  • 更改密码需要重新部署

我发现最好的办法是检查一个configuration,使用混合文档默认值和占位符来部署特定的数据。 我们的应用程序总是寻找一个允许覆盖任何variables的系统configuration。 这允许生产机器具有适合其部署的configuration。

注意:当我作为pipe理员工作时,我总是从代码中分别pipe理configuration(出于很好的理由)。

我总是会排除包含密码或其他访问细节(如数据库)的重要configuration文件,这是纯粹的最佳做法。 另外,在源代码和版本控制的基础上,通常会有多个用户,而不是所有用户都使用相同的数据库细节,甚至使用相同的服务器configuration(域等),为此configuration文件应该被排除在外很多。

没有一个适当的构build过程,我使用这个策略(对于PHP应用程序):

  1. 创build一个文件夹/etc/companyname
  2. 其中,放置两个文件:

     <?php // env.php return 'prod'; 
     <?php // appname-prod.php return array( 'db' => array( /* credentials */ ), /* other host-specific conf data */ ); 
  3. 使这两个文件只能由您的PHP进程读取

现在你的应用程序的configuration文件将是这样的:

 <?php // config.php $env = (require "/etc/companyname/env.php"); $creds = (require "/etc/companyname/appname-{$env}.php"); 

有了这个,环境定义了所使用的凭证,并且可以在预configuration的环境之间移动代码(并用$env控制某些选项)。 当然,这可以通过服务器环境variables来完成,但是a)设置起来更简单,并且b)不向服务器上的每个脚本公开凭证(不会像phpinfo() )。

为了更容易在PHP之外阅读,您可以创build凭据文件JSON或其他东西,只需要轻微的性能下降(APC将不会caching它们)。

我更喜欢在主设置文件旁边有一个local_settings文件。 这个local_settings不应该被添加到版本库,但是我将添加一个sample.local_setting到版本库来显示这个文件的结构。

在运行时,如果local_settings存在,它的值将覆盖主设置文件的值。

例如在python中:

settings.py:

 log='error.log' db=lambda:None db.host='localhost' db.user='' db.password='' try: import local_settings except ImportError: pass 

local_settings.py:

 from settings import * db.user='abcd' db.password='1234'