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


134

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

但是像其他设置一样,密码似乎应该进行版本控制。那么保持密码版本控制的正确方法什么?

我想这将涉及保持秘密,在自己的“秘密设置”文件,并有文件的加密和版本控制。但是什么技术呢?以及如何正确执行此操作?有没有更好的方法可以完全解决这个问题?


我通常会问这个问题,但是在我的特定实例中,我想使用gitgithub存储Django / Python站点的秘密密钥和密码。

同样,当我使用git推/拉时,理想的解决方案也会做一些神奇的事情-例如,如果加密的密码文件更改了运行的脚本,该脚本会要求输入密码并将其解密到位。


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


1
实际上,要预先筹集一些资金以使整个回购协议保持私有。
约翰·米

29
@JohnMee我实际上已经为私有存储库付费,但是重点仍然是-您不应该在存储库中保留敏感信息。
克里斯·W

1
我认为很难获得令人满意的答案的主要原因是,用于连接数据库的老式纯文本密码是不那么敌对时代的遗物。正确的答案是类似“您的代码不需要秘密”之类的东西,但是您所访问的系统却没有给您太多选择。
msw 2012年

4
为什么?外部服务的版本控制密码中有zilch值。版本控制的主要价值在于,您可以检查应用程序的已知历史修订版本,这些历史修订版本处于正常运行状态并运行它们。但是,旧密码对您没有用。如果他们被撤销,他们将再也无法工作。
Panic Panic

Answers:


100

您完全想在保持版本控制的同时加密敏感设置文件是正确的。正如您提到的,最好的解决方案是Git在推送某些敏感文件时透明地对其进行加密,以便在本地(即在具有证书的任何计算机上)可以使用设置文件,但Git或Dropbox或在VC下存储文件无法读取纯文本信息。

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

这个要点https://gist.github.com/873637显示了有关如何将Git的smudge / clean过滤器驱动程序与openssl一起使用的教程,以透明方式加密推送的文件。您只需要进行一些初始设置即可。

工作原理摘要

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

clean_filter_openssl 
smudge_filter_openssl 
diff_filter_openssl 

Git用于解密,加密和支持Git差异。在这些脚本中定义了主密码和盐(已修复!),您必须确保.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_ssl和类似diff_filter_oepnssl。参见要点。

您的带有敏感信息的仓库应该有一个.gitattribute文件(未加密并包含在仓库中),该文件引用.gitencrypt目录(该目录包含Git透明地加密/解密项目所需的所有内容),并且该文件存在于本地计算机上。

.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

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

笔记

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

如果攻击者可以访问许多加密的存储库/文件,则在理论上使用固定的密码短语可能会导致暴力破解漏洞。海事组织,这种可能性非常低。正如本教程底部提到的那样,不使用固定密码短语将导致不同机器上的本地回购版本始终显示“ git status”已发生更改。


1
哦,很有趣。这听起来几乎和我想要的完全一样(除了加密整个存储库之外)。
克里斯·W

您可以将多个应用程序的所有敏感设置文件保存在一个加密存储库中,也可以将包含敏感设置的加密存储库作为Git子模块添加到您的项目中,如git-scm.com/book/en/Git-Tools-Submodules所述。
dgh 2012年

在(加密的)子模块中存储生产密码/设置并不少见。stackoverflow.com/questions/11207284/…。甚至可以更轻松地管理跨项目的设置。
dgh

值得检查github.com/AGWA/git-crypt以获取更新的解决方案。它具有允许对单个文件进行加密的优点,并且声称“在语义上具有可证明的安全性”。要点的作者本人建议在github.com/shadowhand/git-encrypt上使用此工具更好。
geekley

52

Heroku推动使用环境变量进行设置和密钥:

处理此类config var的传统方法是将它们放在源代码下-放在某种属性文件中。这是一个容易出错的过程,对于经常需要使用应用程序特定配置维护单独的(和私有的)分支的开源应用程序而言,这尤其复杂。

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

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


就个人而言,我认为将秘密密钥与代码一起保存是错误的。从根本上说,它与源代码管理不一致,因为密钥是针对代码外部的服务。一个好处是开发人员可以克隆HEAD并运行应用程序而无需进行任何设置。但是,假设开发人员检查了该代码的历史版本。他们的副本将包含去年的数据库密码,因此该应用程序将无法使用今天的数据库。

使用上面的Heroku方法,开发人员可以签出去年的应用程序,使用今天的密钥对其进行配置,然后针对当今的数据库成功运行它。


1
这个答案没有引起足够的重视,但与Linux方式最吻合。
Nikolay Fominyh,2012年

11
因此,如果在您的bashrc中设置了环境变量,并且要部署新服务器,那么是什么创建了bashrc?只是将密码从您的源代码存储库中移出,然后移到您的部署配置中吗?(大概也在源代码存储库中还是在其自己的存储库中?)
Jonathan Hartley

@JonathanHartley您的.bashrc不应位于Django应用的代码存储库中。
史蒂夫

4
抱歉,我的评论含糊,但这是因为我确实感到困惑。我喜欢此答案的观点,但从未完全理解它。如果我要部署到几个不同的环境中,每个环境都包含多个主机,也许还有几种类型的主机,那么显然我需要自动创建将存在于每个主机上的.bashrc文件,以设置其环境变量。那么答案是说我应该有第二个仓库,与我的源头分开,它包含所有设置,这些设置将在部署时成为.bashrc中的环境变量?
乔纳森·哈特利

1
只需在部署PER计算机后对其进行配置。如果您的部署过程是“引导新计算机,然后再将流量重定向到该计算机,然后再将其发送到头部,然后将其射杀”,这是IMHO的最佳做法,那么您确实需要自动创建所有环境变量
乔纳森·哈特利

16

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

我建议阅读“十二要素应用程序 ”的配置章节,如果您有兴趣的话,也阅读其他章节。


6
似乎环境变量是使用秘密设置运行应用程序的好方法……但是它仍然无法回答这些设置保留在何处的问题。
克里斯·W

2
通常,每个应用程序都应该有一个README文件。在其中,指定应设置的环境变量,并且每次部署项目时,只需按照以下步骤进行设置即可。您还可以创建带有许多的Shell脚本export MY_ENV_VAR=,并且在部署时,只需为其填充正确的值和source它。如果保持你的意思版本的设置,你不应该摆在首位这样做。
Samy Dindane 2012年

另外,支持“十二要素”应用程序-确实很棒。
克里斯·W

4
@Samy:如果您已经进行了自动部署?
乔纳森·哈特利

3
@Samy我仍然不明白如何设置环境变量。12个因素的应用程序页面也不清楚(除非您使用的是Heroku,而我当前的项目不在此)。我们是说生成脚本需要询问中央配置存储“我是X机,请给我我的配置数据”,然后以应该设置的环境变量的值作为响应。在那种情况下,我认为您不再需要生成的脚本。我在这里疯狂地猜测,我在吠叫正确的树吗?
乔纳森·哈特利

10

一种选择是将项目绑定的凭据放入加密的容器(TrueCrypt或Keepass)中并推送。

从下面的评论中更新为答案:

有趣的问题顺便说一句。我刚刚发现了这一点:github.com/shadowhand/git-encrypt对于自动加密而言,它看起来很有希望


拥有我可以自动化的东西会很好。这样,如果我的加密密码文件发生更改,它将自动解密新文件。
克里斯·W

7
有趣的问题顺便说一句。我刚刚发现了这一点:github.com/shadowhand/git-encrypt对于自动加密而言,它看起来很有希望。
schneck

1
哇太好了 git-encrypt声音的描述与我正在寻找的完全一样。“当使用托管在第三方存储服务器上的远程git存储库时,有时会担心数据机密性。本文将逐步指导您设置git存储库其本地工作目录正常(未加密),但提交的内容已加密。” (当然,我只希望对我的内容的一部分进行加密...)
Chris W.

@schneck将您的评论作为答案发布,以便Chris可以接受它-听起来这就是他在寻找的东西。
Tony Abou-Assaleh,2012年

9

我建议为此使用配置文件,而不要对其进行版本控制。

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

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


1
但是,该怎么存储规范密码记录呢?让这些数据仅位于机器上的配置文件中(这可能有一天会崩溃)令我感到不安。
克里斯·W

@ChrisW。如果机器崩溃了,则不必再使用密码了……但是,如果生产机器上只有一个数据副本,那应该会出现一个红色标记。但这并不意味着它应该在VCS中。应该有RAID,完整备份,并在磁和光介质上进行增量备份。许多公司都有变更控制程序,该程序可能会规定如何以及在何处将密码和其他敏感材料存储在纸上。
史蒂夫·布佐纳斯

@ChrisW我不想粗鲁,但似乎您没有告诉我们真相,您要存储的密码不是在Development中使用的,而是在生产中使用的。这不是真的吗?否则,为什么还要照顾开发或测试机器以及开发密码?没有人会那样做。
tiktak

顺便说一句,在我们公司,所有开发密码都可以在纸上和Intranet上获得。因为他们没有价值。之所以在这里,是因为我们开发的软件需要身份验证。
tiktak

@tiktak,您是正确的-我的问题是关于生产密码的处理方法。我并不特别在乎将开发密码存储在A VCS中。抱歉,如果我还不够清楚的话。
克里斯·W

7

BlackBox是由StackExchange最近发布的,虽然我尚未使用它,但它似乎可以完全解决问题并支持此问题中要求的功能。

https://github.com/StackExchange/blackbox的描述中:

将机密安全存储在VCS仓库中(即Git或Mercurial)。这些命令使您可以轻松地对存储库中的特定文件进行GPG加密,以便在存储库中对它们进行“静态加密”。但是,使用脚本可以轻松地在需要查看或编辑脚本时对其进行解密,并可以将其解密以用于生产。


7

自从问了这个问题之后,我就确定了一个解决方案,该解决方案是在与一小群人一起开发小型应用程序时使用的。

git-crypt

git-crypt使用GPG对文件名进行匹配的透明加密。例如,如果您添加到.gitattributes文件中...

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

...然后,像这样的文件config.secret.json将始终通过加密方式推送到远程存储库,但在本地文件系统上保持未加密状态。

如果我想向您的存储库添加一个新的GPG密钥(一个人),可以解密受保护的文件,请运行git-crypt add-gpg-user <gpg_user_key>。这将创建一个新的提交。新用户将能够解密后续提交。


5

我通常会问这个问题,但是在我的特定实例中,我想使用git和github存储Django / Python站点的秘密密钥和密码。

不,只是不要,即使这是您的私人存储库,并且您从不打算共享它,也不要。

您应该创建一个local_settings.py,将其放在VCS上忽略,然后在settings.py中执行类似的操作

from local_settings import DATABASES, SECRET_KEY
DATABASES = DATABASES

SECRET_KEY = SECRET_KEY

如果您的机密设置用途广泛,我很想说您做错了


9
但是我仍然需要在某个地方跟踪那些秘密。例如,通过密码或其他方法,对吗?
克里斯·W

存储私人数据的法规和实施取决于项目所在公司的政策。我非常怀疑该项目的源代码是否正确,因为任何第三方测试人员或程序员都可以看到它们
Hedde van der Heide

4

编辑:我假设您想跟踪以前的密码版本-例如,对于防止密码重用等的脚本。

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

  1. 您将密钥保留在本地计算机上。
  2. 您将'mypassword'添加到忽略的文件。
  3. 在pre-commit挂钩上,您将mypassword文件加密为git跟踪的mypassword.gpg文件,并将其添加到提交中。
  4. 在合并后钩子上,您只需将mypassword.gpg解密为mypassword。

现在,如果您的“ mypassword”文件未更改,则将使用相同的密文对其进行加密,并且不会将其添加到索引中(无冗余)。稍加修改mypassword会导致密文截然不同,并且登台区域中的mypassword.gpg与存储库中的密文有很大不同,因此将被添加到提交中。即使攻击者掌握了您的gpg密钥,他仍然需要暴力破解密码。如果攻击者可以使用密文访问远程存储库,则他可以比较一堆密文,但是它们的数量不足以给他带来任何不可忽视的优势。

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

您也可以为不同类型的密码等设置单独的密钥。


3

通常,我将密码分隔为配置文件。并使其成为dist。

/yourapp
    main.py
    default.cfg.dist

当我跑步时main.py,输入真实密码default.cfg该副本中。

ps。当您使用git或hg时。您可以忽略*.cfg要制作的文件.gitignore.hgignore


我正在谈论.dist文件:实际配置文件的示例。一个好的做法是,只有通过删除“ .dist”扩展名(或更优:复制)才能重命名来运行该软件,也就是说,您应该能够在几秒钟内试用该软件,而无需在安装过程中对其进行配置一整天。
tiktak

3

提供一种方法来覆盖配置

这是管理您签入的配置的一组合理默认值的最佳方法,而无需完成配置或包含主机名和凭据之类的内容。有几种方法可以覆盖默认配置。

环境变量(如其他人已经提到的)是做到这一点的一种方法。

最好的方法是寻找一个覆盖默认配置值的外部配置文件。这使您可以通过诸如Chef,Puppet或Cfengine之类的配置管理系统来管理外部配置。配置管理是与代码库分开进行配置管理的标准答案,因此您无需发布即可在单个主机或一组主机上更新配置。

仅供参考:加密凭证并非总是最佳做法,尤其是在资源有限的地方。可能情况是,加密凭据不会为您带来额外的风险缓解,而只会增加不必要的复杂性。在做出决定之前,请确保您进行了正确的分析。


2

使用例如GPG加密密码文件。将密钥添加到本地计算机和服务器上。解密文件并将其放在您的repo文件夹之外。

我使用homefolder中的passwords.conf。在每次部署中,此文件都会更新。


然后,软件需要解密密码文件。
tiktak

好吧,只有在部署站点时,密码才会解密并写入纯文本密码文件
Willian

2

不可以,私钥和密码不属于版本控制。当大多数情况下并非所有人都应该有权访问这些服务时,没有理由让每个人都拥有对存储库的读取访问权的负担,因为他们知道生产中使用的敏感服务凭证。

从Django 1.4开始,您的Django项目现在附带了project.wsgi定义application对象的模块,这是开始强制使用a的理想场所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()

现在,您可以拥有一个local.py可以配置所有者和组的模块,以便只有授权人员和Django进程才能读取文件的内容。


2

如果您需要VCS作为秘密,则至少应将其保存在与实际代码分开的第二个存储库中。因此,您可以授予团队成员访问源代码存储库的权限,而他们不会看到您的凭据。此外,将此存储库托管在其他位置(例如,在具有加密文件系统的您自己的服务器上,而不是在github上),并将其检出到生产系统中,可以使用git-submodule之类的东西。


1

另一种方法可能是完全避免在版本控制系统中保存机密,而是使用诸如hashicorp的Vault之类的工具,该工具是具有密钥滚动和审核,具有API和嵌入式加密功能的秘密存储。


1

这是我的工作:

  • 将所有秘密保留为$ HOME / .bashrc来源的$ HOME / .secrets(go-r权限)中的env vars(这样,如果您在某人面前打开.bashrc,他们将看不到这些秘密)
  • 配置文件作为模板存储在VCS中,例如config.properties存储为config.properties.tmpl
  • 模板文件包含机密的占位符,例如:

    my.password = ## MY_PASSWORD ##

  • 在应用程序部署中,将运行脚本,该脚本将模板文件转换为目标文件,并用环境变量的值替换占位符,例如将## MY_PASSWORD ##更改为$ MY_PASSWORD的值。


0

如果您的系统提供了此功能,则可以使用EncFS。因此,您可以将加密的数据保留为存储库的子文件夹,同时为应用程序提供对已装入数据的解密视图。由于加密是透明的,因此在拉或推上不需要任何特殊操作。

但是,它将需要挂载EncFS文件夹,这可以由您的应用程序根据存储在版本化文件夹之外的其他位置的密码(例如,环境变量)来完成。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.