如何将密钥信息排除在 Git 存储库之外

How to keep secret key information out of Git repository

提问人:BlueMoon93 提问时间:9/12/2018 更新时间:8/27/2021 访问量:9025

问:

我的存储库中有一些文件,其中一个包含一个秘密的 Adafruit 密钥。我想使用 Git 来存储我的存储库,但我不想发布密钥。

保密的最佳方法是什么,而不必在我每次提交和推送某些内容时都将其清空?

git 密钥

评论

0赞 Ian Kenney 9/12/2018
根据您的用例,一种方法是从环境中读取密钥,而不是将其直接包含在文件中。这使配置与代码分开。12factor.net 是一本好书。
0赞 Pramod Patil 9/12/2018
将所有文件/文件夹名称放在 .gitIgnore 文件中。Git 不会推送此处提到的任何文件或文件夹,只能用于开发目的。
0赞 Pramod Patil 9/12/2018
查看此链接以配置 git ignore

答:

6赞 Marcin Pietraszek 9/12/2018 #1

根据您要实现的目标,您可以选择以下方法之一:

  • 将文件保留在由 git 管理的树中,但使用 ingitignore
  • 将文件内容保持在环境变量中,
  • 不要使用带密钥的文件,将密钥内容保存在其他地方(外部系统,如 HashiCorp 的保险库、数据库、云(不建议这样做,我不建议这样做)等)

第一种方法很简单,不需要太多工作,但你仍然会遇到将密钥传递到不同位置的问题,在那里你可以安全地使用同一个存储库。第二种方法需要更多的工作,与第一种方法具有相同的缺点。

第三个肯定比第一个和第二个需要更多的工作,但可能会导致一个非常安全的设置。

3赞 Mark Bramnik 9/12/2018 #2

这在很大程度上取决于项目的要求

基本上,最好的安全策略是不要在源代码管理系统中存储密钥、密码和任何易受攻击的信息。如果这是目标,则有许多不同的方法:

  • 在运行时“提供”此类信息并将其保存在其他地方:

    ./runMyApp.sh -db.password=

  • 使用专用工具(例如 Hashicorp 的 Vault)来管理机密

  • 离线对密钥值进行编码,并将编码后的值存储在 git 中。如果没有用于解码的密钥,仅此编码值是无用的。在运行时再次解码值,使用某种共享密钥 infra / 非对称密钥对,在这种情况下,您可以使用公钥进行编码,私钥进行解码
1赞 Obsidian 9/13/2018 #3

我想使用 Git 来存储我的存储库,但我不想发布密钥。

对于像密钥这样关键的东西,我会使用位于开发环境之外的专用密钥环基础设施,可以选择与密钥密码耦合。

撇开这种情况不谈,我个人为此使用子模块。退房:

git submodule

具体而言,我声明了一个全局 Git 存储库,其中我依次声明了另一个 Git 存储库,该存储库将包含将要公开的实际项目。这使我们能够在顶层存储与给定项目相关的所有内容,但不会强制与之相关且不会发布。例如,这可能是我所有的草稿、自动化脚本、工作笔记、项目规范、测试、错误报告等。

在这个工具提供的所有优势中,我们可以强调这样一个事实,即您可以将一个已经存在的存储库声明为子模块,该存储库位于父存储库的内部或外部。

真正有趣的是,主存储库和子模块仍然是不同的 Git 存储库,仍然可以独立配置。这意味着您不需要为父存储库配置其远程服务器。

这样一来,无论您在哪里工作,您都可以获得版本控制系统的所有好处,同时仍然确保自己永远不会意外地将未存储在公共子模块中的内容推到外部。

1赞 openCivilisation 6/1/2020 #4

如果您没有太多的机密需要管理,并且您确实希望将机密保留在版本控制中,我会将父存储库设为私有。它包含 2 个文件夹 - 一个 secrets 文件夹和一个用于公共存储库的 gitsubmodule(在另一个文件夹中)。我使用 ansible crypt 来加密 secrets 文件夹中的任何内容,并使用 bash 脚本来传递解密的内容,并将这些 secret 加载为环境变量,以确保 secrets 文件始终保持加密状态。

Ansible crypt 可以加密和解密环境变量,我将其包装在 bash 脚本中以执行这些功能,例如 -

testsecret=$(echo 'this is a test secret' | ./scripts/ansible-encrypt.sh --vault-id $vault_key --encrypt)
result=$(./scripts/ansible-encrypt.sh --vault-id $vault_key --decrypt $testsecret)
echo $result

testsecret 这里是加密的 base64 结果,可以安全地存储在文本文件中。稍后,您可以获取该文件以将加密结果保存在内存中,最后,当您需要使用密钥时,您可以解密它./scripts/ansible-encrypt.sh --vault-id $vault_key --decrypt $testsecret

上面引用的这个 bash 脚本如下 (ansible-encrypt.sh)。它以一种可以在 base64 中存储加密变量的方式包装 ansible crypt 函数,从而解决了编码中可能出现的一些问题。

#!/bin/bash

# This scripts encrypts an input hidden from the shell and base 64 encodes it so it can be stored as an environment variable
# Optionally can also decrypt an environment variable

vault_id_func () {
    if [[ "$verbose" == true ]]; then
        echo "Parsing vault_id_func option: '--${opt}', value: '${val}'" >&2;
    fi
    vault_key="${val}"
}
secret_name=secret
secret_name_func () {
    if [[ "$verbose" == true ]]; then
        echo "Parsing secret_name option: '--${opt}', value: '${val}'" >&2;
    fi
    secret_name="${val}"
}
decrypt=false
decrypt_func () {
    if [[ "$verbose" == true ]]; then
        echo "Parsing secret_name option: '--${opt}', value: '${val}'" >&2;
    fi
    decrypt=true
    encrypted_secret="${val}"
}

IFS='
'
optspec=":hv-:t:"

encrypt=false

parse_opts () {
    local OPTIND
    OPTIND=0
    while getopts "$optspec" optchar; do
        case "${optchar}" in
            -)
                case "${OPTARG}" in
                    vault-id)
                        val="${!OPTIND}"; OPTIND=$(( $OPTIND + 1 ))
                        opt="${OPTARG}"
                        vault_id_func
                        ;;
                    vault-id=*)
                        val=${OPTARG#*=}
                        opt=${OPTARG%=$val}
                        vault_id_func
                        ;;
                    secret-name)
                        val="${!OPTIND}"; OPTIND=$(( $OPTIND + 1 ))
                        opt="${OPTARG}"
                        secret_name_func
                        ;;
                    secret-name=*)
                        val=${OPTARG#*=}
                        opt=${OPTARG%=$val}
                        secret_name_func
                        ;;
                    decrypt)
                        val="${!OPTIND}"; OPTIND=$(( $OPTIND + 1 ))
                        opt="${OPTARG}"
                        decrypt_func
                        ;;
                    decrypt=*)
                        val=${OPTARG#*=}
                        opt=${OPTARG%=$val}
                        decrypt_func
                        ;;
                    encrypt)
                        encrypt=true
                        ;;
                    *)
                        if [ "$OPTERR" = 1 ] && [ "${optspec:0:1}" != ":" ]; then
                            echo "Unknown option --${OPTARG}" >&2
                        fi
                        ;;
                esac;;
            h)
                help
                ;;
            *)
                if [ "$OPTERR" != 1 ] || [ "${optspec:0:1}" = ":" ]; then
                    echo "Non-option argument: '-${OPTARG}'" >&2
                fi
                ;;
        esac
    done
}
parse_opts "$@"

if [[ "$encrypt" = true ]]; then
    read -s -p "Enter the string to encrypt: `echo $'\n> '`";
    secret=$(echo -n "$REPLY" | ansible-vault encrypt_string --vault-id $vault_key --stdin-name $secret_name | base64 -w 0)
    unset REPLY
    echo $secret
elif [[ "$decrypt" = true ]]; then
    result=$(echo $encrypted_secret | base64 -d | /snap/bin/yq r - "$secret_name" | ansible-vault decrypt --vault-id $vault_key)
    echo $result
else
    # if no arg is passed to encrypt or decrypt, then we a ssume the function will decrypt the firehawksecret env var
    encrypted_secret="${firehawksecret}"
    result=$(echo $encrypted_secret | base64 -d | /snap/bin/yq r - "$secret_name" | ansible-vault decrypt --vault-id $vault_key)
    echo $result
fi

将加密值存储为环境变量比解密静态内容并将明文结果保留在内存中要安全得多。对于任何过程来说,这都非常容易被抽走。

如果只想与他人共享代码而不是机密,则可以将 git 模板用于父私有存储库结构,以便其他人可以继承该结构,但使用自己的机密。这也允许 CI 获取测试所需的一切。

或者,如果您不希望您的机密处于版本控制中,则只需在包含机密的包含文件夹上使用 git ignore 即可。

就个人而言,这让我感到紧张,用户错误仍然可能导致公开提交的机密,因为这些文件仍在公共存储库的根目录下,任何数量的事情都可能出错,这可能会使这种方法感到尴尬。

0赞 Ega 8/27/2021 #5

对于 Django 添加另一个名为 secrets.py 或任何您想要的文件,以及另一个名为 .gitignore 的文件

在 .gitignore 中键入 secrets.py

将密钥粘贴到 secrets.py 文件中,并使用

from foldername.secrets import * 

这对我有用。