using secureconfig with multiple config files
I thought perhaps I’d start sharing some of the idioms I’ve developed whilst working with secureconfig — particularly the SecureConfigParser library.
Because of ConfigParser’s handy aggregation of `sections` from several config files, one way of making config file management easier is to split off all of the “credentials” and other sensitive data — passwords, api tokens, etc — into a separate config file. Then I encrypt those credentials and (usually) don’t ever have to touch them.
But, splitting sensitive data away from other variables may not make organizational sense. You may have different details for “development” and “production” sets of configurations.
For my projects, I have standardized to using the “.insecure” extension to indicate an ini config that hasn’t had its sensitive data encrypted yet. This practice helps me avoid accidentally committing credentials to the source repo simply by adding a line like the following to my .hgignore or .gitignore file:
Having done that, I can use an iPython shell or a short python script to encrypt those variables I want encrypted, and then write it back to the config file:
from secureconfig import SecureConfigParser # upon first run, my key doesn't actually exist yet -- I let # SecureConfigParser generate one for me. scfg = SecureConfigParser.from_file('.keys/demo.key') scfg.read('demo.ini.insecure') pwd = scfg.read('credentials', 'password') token = scfg.read('api', 'token') scfg.set('credentials', 'password', pwd, encrypt=True) scfg.set('token', 'token', token, encrypt=True) scfg.write(open('demo.ini', 'w'))
But I’m lazy, and I like using Fabric for automated deployment. I want all variables matching certain names — particular “password” and “token” — to be encrypted every time I make a change to any .ini.insecure file.
Here’s a function I call via fab protect that does the following — although you can just as easily strip away the Fabric specific stuff for another deployment library:
- gets a list of all config files with the “insecure” extension
- reads each config in separately and iterates through every option
- compares each option against a manually entered list of variable names that should be encrypted
- uses .set method with encrypt=True to set the specified variables back into the config, encrypted
- writes config files back into the same directory but without the .insecure extension.
from __future__ import print_function import os from fabric.decorators import task from secureconfig import SecureConfigParser KEYPATH_LOCAL = '.keys/super_secret_key' CONFIG_DIR_LOCAL = 'etc/configs' INSECURE_CONFIG_FILES = [x for x in os.listdir(CONFIG_DIR_LOCAL) if x.endswith('insecure')] VARS_TO_PROTECT = ['token', 'password'] @task def protect(keypath=KEYPATH_LOCAL): 'generate safe *.ini files from *.ini.insecure files' for cfgfile in INSECURE_CONFIG_FILES: configpath = os.path.join(CONFIG_DIR_LOCAL, cfgfile) scfg = SecureConfigParser.from_file(keypath) scfg.read(configpath) #'amqp.ini.insecure') for sec in scfg.sections(): for opt in scfg.options(sec): if opt in VARS_TO_PROTECT: val = scfg.get(sec, opt) scfg.set(sec, opt, val, encrypt=True) outpath = configpath.rstrip('insecure')[:-1] print("Writing %s (don't forget to commit changes to the repo!)" % outpath) scfg.write(open(outpath, 'w'))
Post here if you have any trouble using this!