I am working on a python project that involves securely retrieving and storing information in internet-accessible locations such as databases and storage buckets. Depending on various factors, I may run code either on a local machine or on hundreds of virtual machines at Amazon AWS or Google Compute Engine.
I use a Github repo to maintain version control and to facilitate access to the code by these virtual machines. I would prefer not to store credentials such as passwords in a flat file in the repository even though the repository is private. However, I don't know how else to automatically provide the necessary credentials to all of the various machines that need them. Storing the credentials in the virtual machine images is suboptimal because I do not want to create a new image whenever a credential changes.
Is there a lightweight solution for distributing credentials of this type to disparate computing environments?
I've used symmetric ciphers with long passwords for this sort of thing when a human accessing any of the collection of machines needs access to facilities on S3. Fully automated, I doubt any of this is very safe. It is more for human intervention.
There is a plaintext file .s3cfg
containing Amazon S3 credentials needed by the tool s3cmd
. We want to back it up to ciphertext file .gpgs3
and have it available in various places, along with some scripts to make it easier for non-gpg users to convert it to the needed .s3cfg
file if they know the password.
I put the gpg commands for the encryption in some short scripts.
Encryption script s3lock.sh
#!/bin/bash
umask 077
gpg --symmetric <~/.s3cfg >~/.gpgs3
Decryption script s3on.sh
#!/bin/bash
umask 077
gpg --decrypt ~/.gpgs3 >~/.s3cfg 2>/dev/null
Tidy-up script s3off.sh
#!/bin/bash
# remove the plain text file
rm -f ~/.s3cfg
This methodology hasn't been subjected to any peer review, but I've yet to have a problem with it.
Some things that could happen with this setup:
s3off.sh
when you are done, leaving the plaintext file available and later when someone cracks your box they get the S3 credentials. They run up a huge bill, Amazon locks down your stuff, and the collections department calls daily. s3off.sh
in a script, but an impatient person interrupts the script, and later when someone cracks your box they get the S3 credentials... and delete all those backups that are in S3 for "safekeeping". Hmm - I'm new, but is this request really off topic? It seems ok to me...
If you use Python and GIT, then we coded up a solution for storing passwords (and other config info) as regular variables, using RC4. It made it easy for us to maintain passwords in plaintext on dev machines, have the passwords encrypted automatically, and an encrypted version distributed automatically with every git push. You need to manually install a private key on each production machine, but thereafter git pulls on the production machine will pull in an updated encrypted file and automatically decrypt it at runtime for use within your code.
One nice feature of the code is that a decrypted (plaintext) copy of your passwords never, ever hits the hard drive on a production machine. And yet you can directly reference your passwords within your python code, and most intellisense systems (such as pyCharm's) can view the password variables during coding on the production machine. It is also very easy to change passwords - just update the plaintext file and git push.
I haven't contributed anything to SO yet, so I might as well start now. I put the code, along with a complete ReadMe, on a GitHub repo:
After grabbing the security.py file, implementation is as simple as:
import security
for line in security.secure(): exec line in globals()
There is a complete README.md file and helloworld.py example in the github repo. Here is the implementation code in case someone wants to comment:
"""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
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.