如何在我的版本控制系统中安全地保存密钥和密码?

我保留了我的版本控制系统中的重要设置,例如开发和生产服务器的主机名和端口。 但是我知道在VCS存储库中保存秘密 (如私钥和数据库密码)是不好的做法

但是,像其他任何设置一样,密码看起来应该是版本化的。 那么保持密码版本控制的正确方法什么?

我想这将涉及保持秘密在他们自己的“秘密设置”文件,并有文件encryption和版本控制。 但是什么技术? 而如何正确地做到这一点? 有没有更好的办法去完成呢?


我通常会问这个问题,但在我的具体情况下,我想使用gitgithub存储Django / Python站点的密钥和密码。

而且,当我用git推/拉时,一个理想的解决scheme会做一些神奇的事 – 例如,如果encryption的密码文件改变了,那么运行一个要求input密码并将其解密的脚本。


编辑:为了清楚起见,我问的是在哪里存储生产的秘密。

你完全正确的想要encryption你的敏感设置文件,同时仍然维护版本控制文件。 正如你所提到的,最好的解决scheme是在你推送某些敏感文件时,Git会透明地encryption这些文件,以便本地(即在任何拥有你的证书的机器上)可以使用设置文件,但Git或Dropbox或者任何人将文件存储在VC下不能以纯文本forms读取信息。

推/拉期间的透明encryption/解密教程

这个要点https://gist.github.com/873637显示了一个关于如何在openssl中使用Git的smudge / cleanfilter驱动来透明地encryption推送文件的教程。 你只需要做一些初始设置。

它如何工作的总结

你将基本上创build一个包含3个bash脚本的.gitencrypt文件夹,

 clean_filter_openssl smudge_filter_openssl diff_filter_openssl 

Git使用它来解密,encryption和支持Git diff。 在这些脚本中定义了一个主密码和salt(固定!),你必须确保.gitencrypt从来没有被实际推送过。 示例clean_filter_openssl脚本:

 #!/bin/bash SALT_FIXED=<your-salt> # 24 or less hex characters PASS_FIXED=<your-passphrase> openssl enc -base64 -aes-256-ecb -S $SALT_FIXED -k $PASS_FIXED 

类似于smudge_filter_open_ssldiff_filter_oepnssl 。 见Gist。

具有敏感信息的仓库应该有.gitattribute文件(未encryption和包含在仓库中),它引用了.gitencrypt目录(其中包含了Git透明地encryption/解密项目所需的所有内容)以及本地计算机上存在的文件。

.gitattribute内容:

 * filter=openssl diff=openssl [merge] renormalize = true 

最后,您还需要将以下内容添加到.git/config文件中

 [filter "openssl"] smudge = ~/.gitencrypt/smudge_filter_openssl clean = ~/.gitencrypt/clean_filter_openssl [diff "openssl"] textconv = ~/.gitencrypt/diff_filter_openssl 

现在,当您将包含敏感信息的存储库推送到远程存储库时,这些文件将被透明地encryption。 当您从具有.gitencrypt目录(包含您的密码)的本地计算机上取出时,这些文件将被透明地解密。

笔记

我应该注意到,本教程没有描述只encryption敏感设置文件的方法。 这将透明地encryption推送到远程VC主机的整个存储库,并对整个存储库进行解密,使其完全在本地解密。 为了达到你想要的行为,你可以把一个或多个项目的敏感文件放在一个sensitive_settings_repo中。 如果您确实需要将敏感文件放在同一个存储库中,则可以调查此透明encryption技术如何与Git子模块配合使用http://git-scm.com/book/en/Git-Tools-Submodules

如果攻击者可以访问许多encryption的Repos /文件,则使用固定密码短语理论上可能导致powershell漏洞。 国际海事组织,这是非常低的概率。 正如本教程底部所提到的,不使用固定密码将会导致不同机器上的本地版本的回购站点总是显示出“git status”发生了变化。

Heroku推送设置和密钥的环境variables的使用 :

处理这种configurationvariables的传统方法是将它们放在源代码中 – 放在某种属性文件中。 这是一个容易出错的过程,对于开源应用程序尤其复杂,因为这些应用程序通常必须使用应用程序特定的configuration来维护单独的(和私有的)分支。

更好的解决scheme是使用环境variables,并将代码保留在外。 在传统主机上或在本地工作,您可以在您的bashrc中设置环境variables。 在Heroku上,你使用config vars。

通过Foreman和.env文件,Heroku提供了令人羡慕的工具链来导出,导入和同步环境variables。


就个人而言,我相信将密钥与代码一起保存是错误的。 它与源代码控制基本上是不一致的,因为密钥是针对代码的外在的服务。 一个好处是开发人员可以克隆HEAD并运行应用程序,而无需任何设置。 但是,假设开发人员检查代码的历史版本。 他们的副本将包括去年的数据库密码,所以应用程序将在今天的数据库上失败。

使用上面的Heroku方法,开发人员可以检出去年的应用程序,使用今天的密钥进行configuration,然后在今天的数据库中成功运行。

我认为最干净的方式是使用环境variables。 例如,您不必处理.dist文件,生产环境中的项目状态将与本地计算机的相同。

如果你有兴趣的话,我build议你阅读十二因子应用程序的configuration章节。

一个选项是将项目绑定的凭据放入一个encryption容器(TrueCrypt或Keepass)中并将其推送。

更新为我的评论下面的答案:

有趣的问题btw。 我刚刚发现这个: github.com/shadowhand/git-encrypt看起来非常有希望用于自动encryption

我build议使用configuration文件,而不是版本。

但是,您可以版本的文件的例子。

我没有看到任何共享开发设置的问题。 根据定义,它不应该包含有价值的数据。

BlackBox最近由StackExchange发布,虽然我还没有使用它,它似乎正好解决了这个问题,并支持这个问题所要求的function。

https://github.com/StackExchange/blackbox上的描述:;

安全地将秘密存储在VCS回购(即Git或Mercurial)中。 这些命令使您可以方便地对GPG中的特定文件进行encryption,使其在存储库中处于“静态encryption”状态。 但是,当您需要查看或编辑这些脚本时,这些脚本可以很容易地解密这些脚本,并解密它们以便在生产中使用。

我通常会问这个问题,但在我的具体情况下,我想使用git和github存储Django / Python站点的密钥和密码。

不,不,即使这是你的私人回购,你从来没有打算分享,不要。

你应该创build一个local_settings.py把它放在VCS上忽略,并在settings.py中做类似的事情

 from local_settings import DATABASES, SECRET_KEY DATABASES = DATABASES SECRET_KEY = SECRET_KEY 

如果你的秘密设置是多才多艺的,我渴望说你做错了什么

编辑:我假设你想跟踪你以前的密码版本 – 比如说,一个脚本,防止密码重用等

我认为GnuPG是最好的方式 – 它已经在一个git相关项目(git-annex)中用于encryption存储在云服务中的存储库内容。 GnuPG(gnu pgp)提供了非常强大的基于密钥的encryption。

  1. 你在你的本地机器上保存一个密钥。
  2. 您将“mypassword”添加到忽略的文件。
  3. 在预提交钩子你encryptionmypassword文件到git跟踪的mypassword.gpg文件,并将其添加到提交。
  4. 在合并后的钩子,你只需将mypassword.gpg解密成mypassword。

现在,如果你的'mypassword'文件没有改变,那么encryption它将得到相同的密文,并且不会被添加到索引(没有冗余)。 对mypassword进行最细微的修改会导致截然不同的密文,并且在临时区域中的mypassword.gpg与存储库中的密码不同,因此会被添加到提交中。 即使攻击者获得了你的gpg密钥,他仍然需要暴力破解密码。 如果攻击者用密文访问远程仓库,他可以比较一堆密文,但是他们的数量不足以给他带来不可忽视的优势。

稍后,您可以使用.gitattributes为您的密码提供退出git diff的即时解密。

你也可以有不同types的密码等单独的密钥

提供一种覆盖configuration的方法

这是最好的方式来pipe理一组理智的默认configuration,而不需要configuration完成,或者包含主机名和凭证等。 有几种方法可以覆盖默认的configuration。

环境variables(如其他人已经提到过的)是这样做的一种方式。

最好的方法是查找覆盖默认configuration值的外部configuration文件。 这使您可以通过Chef,Puppet或Cfengine等configurationpipe理系统pipe理外部configuration。 configurationpipe理是configurationpipe理与代码库分开pipe理的标准答案,因此您不必执行发布来更新单个主机或一组主机上的configuration。

仅供参考:encryption信用并不总是最好的做法,特别是在资源有限的地方。 可能是这样的情况,encryption的信用将得到你没有额外的风险缓解,只是添加一个不必要的复杂层。 确定你做出正确的分析,然后再作出决定。

自从提出这个问题以来,我已经解决了一个解决scheme,我使用这个解决scheme时,与一小群人一起开发小应用程序。

混帐隐窝

git-crypt使用GPG在文件名称匹配某些模式时透明地encryption文件。 对于实例,如果你添加到你的.gitattributes文件…

 *.secret.* filter=git-crypt diff=git-crypt 

…然后像config.secret.json这样的文件将永远被encryption到远程仓库,但在本地文件系统上保持未encryption。

如果我想添加一个新的GPG密钥(一个人)到你的repo,它可以解密受保护的文件,然后运行git-crypt add-gpg-user <gpg_user_key> 。 这会创build一个新的提交。 新用户将能够解密后续的提交。

通常,我将密码分隔成一个configuration文件。 并让他们分开。

 /yourapp main.py default.cfg.dist 

而当我运行main.py ,把真正的密码在default.cfg复制。

PS。 当你使用git或者hg时。 您可以忽略*.cfg文件来制作.gitignore.hgignore

encryption密码文件,例如使用GPG。 在本地机器和服务器上添encryption钥。 解密该文件,并将其放在您的回购文件夹之外。

我使用位于我的homefolder中的passwords.conf。 在每次部署这个文件得到更新。

不,私钥和密码不属于版本控制。 没有任何理由让读者通过读取存储库的知识来了解生产中使用的敏感服务凭证,当时很可能不是所有人都可以访问这些服务。

从Django 1.4开始,您的Django项目现在提供了一个定义application对象的project.wsgi模块,它是开始强制使用包含站点特定configuration的project.local设置模块的理想场所。

此设置模块在版本控制中将被忽略,但在将项目实例作为WSGI应用程序(通常用于生产环境)运行时,此设置模块是必需的。 这是它应该是这样的:

 import os os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.local") # This application object is used by the development server # as well as any WSGI server configured to use this file. from django.core.wsgi import get_wsgi_application application = get_wsgi_application() 

现在你可以configuration一个local.py模块,它的所有者和组可以被configuration,只有经过授权的人员和Django进程才能读取文件的内容。

如果你需要VCS的秘密,你至less应该把它们放在与你实际代码分开的第二个仓库中。 所以你可以让你的团队成员访问源代码库,他们不会看到你的凭据。 此外,在其他地方(例如,在您自己的服务器上使用encryption文件系统,而不是在github上)托pipe这个存储库,并将其检查到生产系统,您可以使用类似git-submodule的东西。

另一种方法可能是完全避免在版本控制系统中保存秘密,而是使用hashicorp中的保险库 ,密钥滚动和审计的秘密存储,API和embedded式encryption等工具。

如果你的系统提供的话,你可以使用EncFS。 因此,您可以将encryption的数据保存为存储库的子文件夹,同时为您的应用程序提供解密的视图,以便将数据放在一边。 由于encryption是透明的,所以在拉或推时不需要特别的操作。

然而,它需要挂载EncFS文件夹,这可以由应用程序根据在版本化文件夹外部存储的密码(例如,环境variables)来完成。