138

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

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

我想这将涉及将秘密保存在他们自己的“秘密设置”文件中,并对该文件进行加密和版本控制。但是什么技术?以及如何正确地做到这一点?有没有更好的方法来解决它?


我一般会问这个问题,但在我的具体实例中,我想使用gitgithub存储Django/Python站点的密钥和密码。

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


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

4

17 回答 17

101

您希望在加密敏感设置文件的同时仍将文件保留在版本控制中,这是完全正确的。正如您所提到的,最好的解决方案是当您推送某些敏感文件时,Git 会透明地加密它们,以便在本地(即在任何拥有您的证书的机器上)您可以使用设置文件,但 Git 或 Dropbox 或任何人将文件存储在 VC 下无法读取明文信息。

Push/Pull 过程中的透明加密/解密教程

这个要点https://gist.github.com/873637显示了一个关于如何使用 Git 的 smudge/clean 过滤器驱动程序和 openssl 来透明地加密推送文件的教程。您只需要进行一些初始设置。

工作原理总结

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

clean_filter_openssl 
smudge_filter_openssl 
diff_filter_openssl 

Git 用于解密、加密和支持 Git diff。在这些脚本中定义了主密码和盐(已修复!),您必须确保 .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 。

如果攻击者可以访问许多加密的存储库/文件,则使用固定密码在理论上可能会导致暴力破解漏洞。IMO,这种可能性非常低。正如本教程底部的注释所述,不使用固定密码将导致不同机器上的本地版本的存储库始终显示“git status”发生了更改。

于 2012-07-26T22:31:55.373 回答
54

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

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

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

Heroku使用 Foreman 和.envfiles 提供了令人羡慕的工具链来导出、导入和同步环境变量。


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

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

于 2012-07-26T10:33:19.997 回答
17

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

我建议您阅读The Twelve-Factor App的配置章节,如果您有兴趣,也可以阅读其他章节。

于 2012-07-20T09:14:07.543 回答
10

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

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

顺便说一句,有趣的问题。我刚刚发现了这个:github.com/shadowhand/git-encrypt看起来非常有希望用于自动加密

于 2012-07-20T08:14:23.957 回答
10

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

但是,您可以对文件的示例进行版本控制。

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

于 2012-07-20T08:20:40.547 回答
8

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

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>​​. 这会创建一个新的提交。新用户将能够解密后续提交。

于 2017-01-13T23:21:59.630 回答
7

BlackBox最近由 StackExchange 发布,虽然我还没有使用它,但它似乎完全解决了问题并支持此问题中要求的功能。

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

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

于 2014-09-03T21:47:39.503 回答
6

我一般会问这个问题,但在我的具体实例中,我想使用 git 和 github 存储 Django/Python 站点的密钥和密码。

不,只是不要,即使它是您的私人仓库并且您从不打算分享它,也不要。

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

from local_settings import DATABASES, SECRET_KEY
DATABASES = DATABASES

SECRET_KEY = SECRET_KEY

如果您的秘密设置如此通用,我很想说您做错了什么

于 2012-07-20T10:32:10.873 回答
4

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

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

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

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

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

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

于 2012-07-20T09:23:43.497 回答
3

通常,我将密码单独作为配置文件。并使它们分布。

/yourapp
    main.py
    default.cfg.dist

当我运行时main.py,将真实密码放入default.cfg复制的密码中。

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

于 2012-07-20T08:38:58.477 回答
3

提供一种覆盖配置的方法

这是为您签入的配置管理一组健全的默认值的最佳方式,无需配置完整或包含主机名和凭据等内容。有几种方法可以覆盖默认配置。

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

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

仅供参考:加密信用并不总是最佳实践,尤其是在资源有限的地方。加密信用可能不会为您带来额外的风险缓解,而只会增加不必要的复杂性。确保在做出决定之前进行适当的分析。

于 2012-07-27T14:32:15.820 回答
2

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

我使用位于我的主文件夹中的 passwords.conf。在每次部署时,此文件都会更新。

于 2012-07-26T09:41:22.227 回答
2

不,私钥和密码不受版本控制。没有理由让每个对您的存储库具有读取权限的人都知道生产中使用的敏感服务凭证,因为很可能并非所有人都应该有权访问这些服务。

从 Django 1.4 开始,您的 Django 项目现在附带一个project.wsgi定义application对象的模块,它是开始强制使用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 进程才能读取文件的内容。

于 2012-07-29T22:27:26.997 回答
2

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

于 2012-07-30T09:50:23.690 回答
1

另一种方法可能是完全避免在版本控制系统中保存机密,而是使用像hashcorp 的 vault这样的工具,这是一种具有密钥滚动和审计功能的机密存储,具有 API 和嵌入式加密。

于 2016-10-06T10:14:51.797 回答
1

这就是我所做的:

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

    my.password=##MY_PASSWORD##

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

于 2018-04-06T21:15:43.607 回答
0

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

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

于 2012-07-29T22:53:42.903 回答