如何在云计算环境中安全地分发凭证?

How can credentials be securely distributed in a cloud computing environment?

我正在从事一个 python 项目,该项目涉及在互联网可访问的位置(例如数据库和存储桶)中安全地检索和存储信息。根据各种因素,我可能 运行 在本地计算机或 Amazon AWS 或 Google Compute Engine 的数百个虚拟机上编码。

我使用 Github 存储库来维护版本控制并方便这些虚拟机访问代码。我不希望将诸如密码之类的凭据存储在存储库的平面文件中,即使存储库是私有的。但是,我不知道还有什么其他方法可以自动为所有需要它们的各种机器提供必要的凭据。将凭据存储在虚拟机映像中不是最佳选择,因为我不想在凭据更改时创建新映像。

是否有一种轻量级解决方案可以将这种类型的凭据分发到不同的计算环境?

一个人访问任何机器集合需要访问 S3 上的设施时,我使用长密码的对称密码来处理这类事情。完全自动化,我怀疑其中任何一个都是非常安全的。它更多的是用于人为干预。

有一个纯文本文件 .s3cfg,其中包含工具 s3cmd 所需的 Amazon S3 凭据。我们想将它备份到密文文件 .gpgs3 并在各个地方提供它,以及一些脚本,使非 gpg 用户更容易将其转换为所需的 .s3cfg 文件,如果他们知道的话密码。

我将用于加密的 gpg 命令放在一些简短的脚本中。

加密脚本s3lock.sh

 #!/bin/bash
 umask 077
 gpg --symmetric <~/.s3cfg >~/.gpgs3

解密脚本s3on.sh

 #!/bin/bash
 umask 077
 gpg --decrypt ~/.gpgs3 >~/.s3cfg 2>/dev/null

整理脚本s3off.sh

 #!/bin/bash
 # remove the plain text file
 rm -f ~/.s3cfg

此方法尚未经过任何同行评审,但我还没有遇到任何问题。

此设置可能会发生的一些事情:

  • 你忘了 运行 s3off.sh 当你完成时,留下明文文件可用,稍后当有人破解你的盒子时,他们会获得 S3 凭据。他们 运行 开出巨额账单,亚马逊锁定你的东西,收款部门每天都打电话。
  • 你把 s3off.sh 放在脚本中,但是一个不耐烦的人打断了脚本,后来当有人破解你的盒子时,他们获得了 S3 凭证......并删除了 S3 中的所有备份 "safekeeping".
  • 一位 disg运行tled 同事注意到这是如何工作的,并在合法访问期间获取明文 S3 凭据...

嗯 - 我是新来的,但这个请求真的离题了吗?我觉得还可以...

如果您使用 Python 和 GIT,那么我们使用 RC4 编写了一个将密码(和其他配置信息)存储为常规变量的解决方案。它使我们可以轻松地在开发机器上以明文形式维护密码,自动加密密码,并在每次 git 推送时自动分发加密版本。您需要在每台生产机器上手动安装私钥,但此后 git 拉取生产机器将拉入更新的加密文件并在运行时自动解密以供您的代码使用。

代码的一个很好的特点是密码的解密(明文)副本永远不会永远进入生产机器上的硬盘驱动器。然而,您可以在 python 代码中直接引用您的密码,并且大多数智能感知系统(例如 pyCharm 的)可以在生产机器上编码期间查看密码变量。更改密码也非常容易 - 只需更新明文文件并 git 推送。

我还没有为 SO 贡献任何东西,所以我不妨现在就开始。我将代码连同完整的自述文件放在 GitHub 存储库中:

https://github.com/KenYounge/security

抓取security.py文件后,实现很简单:

import security
for line in security.secure(): exec line in globals()

github 仓库中有一个完整的 README.md 文件和 helloworld.py 示例。下面是实现代码,以防有人想发表评论:

"""Secure python variables: encrypt, decrypt, import into global namespace."""
__module__    = 'security.py'
__author__    = "Kenneth A Younge"
__copyright__ = "Copyright (c) 2014, Kenneth A. Younge"
__license__   = "GNU General Public License"
__email__     = "kenyounge@gmail.com"

import os

def crypt(data, key):
    x = 0
    box = range(256)
    for i in range(256):
        x = (x + box[i] + ord(key[i % len(key)])) % 256
        box[i], box[x] = box[x], box[i]
    x = 0
    y = 0
    out = []
    for char in data:
        x = (x + 1) % 256
        y = (y + box[x]) % 256
        box[x], box[y] = box[y], box[x]
        out.append(chr(ord(char) ^ box[(box[x] + box[y]) % 256]))
    return ''.join(out)

def secure(file_names=('passwords.py',),
           key_name='security.key',
           key_path='~/',
           pvt_path='_private/',
           verbose=False):
    """Transform files (encrypt and/or decrypt _private files).

    Keyword arguments:
        filenames  --  sequence of file names to encrypt/decrypt
        key_name   --  file name of your personal rc4 encryption key
        key_path   --  location of encryption key on production machines
        pvt_path   --  location of private files and encryption key during dev
        verbose    --   print info

    Defaults:
        filenames  --  passwords.py    currently a tuple with one file
        key_name   --  security.key     
        key_path   --  ~/               
        pvt_path   --  _private/
        verbose    --  False
    """

    # Load key (try production location first)
    if os.path.exists(os.path.join(key_path, key_name)):
        key = open(os.path.join(key_path, key_name), 'r').read()
    elif os.path.exists(os.path.join(
            os.path.dirname(__file__), pvt_path + key_name)):
        key = open(os.path.join(
            os.path.dirname(__file__), pvt_path + key_name), 'r').read()
    else:
        key = open(os.path.join(
            os.path.dirname(__file__), key_name), 'r').read()

    # secure each file
    code_lines = []
    for filename in file_names:

        filename_raw = os.path.join(
            os.path.dirname(__file__), pvt_path + filename)
        filename_rc4 = os.path.join(
            os.path.dirname(__file__),
            os.path.basename(filename).replace('.py', '.rc4'))

        # Encrypt
        try:
            if os.path.exists(filename_raw):
                with open(filename_raw, 'r') as f:
                    text = f.read()
                with open(filename_rc4, 'w') as f:
                    f.write(crypt(str(text).strip(), key).encode('hex'))
                if verbose:
                    print 'Encrypted ' + filename_raw
            else:
                if verbose:
                    print('File (' + filename_raw + ') not found')
        except Exception as e:
            print(str(e))

        # Decrypt
        try:
            if os.path.exists(filename_rc4):
                with open(filename_rc4, 'r') as f:
                    text = crypt(str(f.read()).strip().decode('hex'), key)
                    lines = [str(line).strip() for line in text.splitlines()]
                if lines: code_lines.extend(lines)
                if verbose:
                    print 'Encrypted ' + filename_rc4
            else:
                print('File ' + filename_rc4 + ' not found')
        except Exception as e:
            print(str(e))
    return code_lines