Merge "Fernet keys rotation action"
This commit is contained in:
commit
ec570f0dc5
|
@ -20,7 +20,8 @@ RUN useradd --user-group keystone \
|
|||
&& cp /var/lib/microservices/venv/bin/keystone-wsgi-public /var/www/cgi-bin/keystone/public \
|
||||
&& touch /etc/keystone/fernet-keys/.placeholder \
|
||||
&& chown -R keystone: /etc/keystone /var/www/cgi-bin/keystone /var/log/apache2 /home/keystone \
|
||||
&& chmod -R 500 /etc/keystone/fernet-keys /etc/keystone/credential-keys
|
||||
&& chmod -R 700 /etc/keystone/fernet-keys \
|
||||
&& chmod -R 500 /etc/keystone/credential-keys
|
||||
|
||||
COPY daemon.sh /usr/local/bin/daemon.sh
|
||||
COPY keystone_sudoers /etc/sudoers.d/keystone_sudoers
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
actions:
|
||||
- name: fernet-rotate
|
||||
image: keystone
|
||||
command: "/usr/bin/python /opt/ccp/bin/fernet-manage.py fernet_rotate"
|
||||
files:
|
||||
- path: /opt/ccp/bin/fernet-manage.py
|
||||
content: fernet-manage.py
|
||||
perm: "0700"
|
|
@ -0,0 +1,122 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import argparse
|
||||
import base64
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import six
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import pykube
|
||||
|
||||
GLOBALS_PATH = '/etc/ccp/globals/globals.json'
|
||||
FERNET_DIR = '/etc/keystone/fernet-keys/'
|
||||
|
||||
LOG_DATEFMT = "%Y-%m-%d %H:%M:%S"
|
||||
LOG_FORMAT = "%(asctime)s.%(msecs)03d - %(levelname)s - %(message)s"
|
||||
logging.basicConfig(format=LOG_FORMAT, datefmt=LOG_DATEFMT)
|
||||
LOG = logging.getLogger(__name__)
|
||||
LOG.setLevel(logging.INFO)
|
||||
|
||||
def get_config():
|
||||
LOG.info("Getting global variables from %s", GLOBALS_PATH)
|
||||
with open(GLOBALS_PATH) as f:
|
||||
global_conf = json.load(f)
|
||||
return global_conf
|
||||
|
||||
def get_pykube_client():
|
||||
os.environ['KUBERNETES_SERVICE_HOST'] = 'kubernetes.default'
|
||||
config = pykube.KubeConfig.from_service_account()
|
||||
return pykube.HTTPClient(config)
|
||||
|
||||
def get_secret_definition(name):
|
||||
client = get_pykube_client()
|
||||
obj_dict = {
|
||||
'metadata': {
|
||||
'name': name,
|
||||
'namespace': NAMESPACE
|
||||
}
|
||||
}
|
||||
secret = pykube.Secret(client, obj_dict)
|
||||
return secret
|
||||
|
||||
def read_from_files():
|
||||
keys = filter(
|
||||
lambda name: os.path.isfile(FERNET_DIR + name) and re.match("^\d+$", name),
|
||||
os.listdir(FERNET_DIR)
|
||||
)
|
||||
data = {}
|
||||
for key in keys:
|
||||
with open(FERNET_DIR + key, 'r') as f:
|
||||
data[key] = f.read()
|
||||
if len(keys):
|
||||
LOG.debug("Keys read from files: %s", keys)
|
||||
else:
|
||||
LOG.warn("No keys were read from files.")
|
||||
return data
|
||||
|
||||
def get_keys_data():
|
||||
keys = PROVIDED_KEYS or read_from_files()
|
||||
return dict([(key, base64.b64encode(value.encode()).decode())
|
||||
for (key, value) in six.iteritems(keys)])
|
||||
|
||||
def write_to_files(data):
|
||||
for (key, value) in six.iteritems(data):
|
||||
with open(FERNET_DIR + key, 'w') as f:
|
||||
decoded_value = base64.b64decode(value).decode()
|
||||
f.write(decoded_value)
|
||||
LOG.debug("Key %s: %s", key, decoded_value)
|
||||
LOG.info("%s keys were written", len(data))
|
||||
|
||||
def set_globals():
|
||||
LOG.info("Setting up global variables")
|
||||
global NAMESPACE, SECRET_NAME, PROVIDED_KEYS
|
||||
config = get_config()
|
||||
|
||||
NAMESPACE = config['namespace']
|
||||
LOG.debug("Namespace: %s", NAMESPACE)
|
||||
|
||||
SECRET_NAME = config['keystone']['fernet_secret_name']
|
||||
LOG.debug("Secret name: %s", SECRET_NAME)
|
||||
|
||||
PROVIDED_KEYS = None
|
||||
if 'fernet_keys' in config['keystone']:
|
||||
PROVIDED_KEYS = config['keystone']['fernet_keys']
|
||||
LOG.debug("Fernet keys: %s", PROVIDED_KEYS)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('command', choices=['fernet_setup', 'fernet_rotate'])
|
||||
args = parser.parse_args()
|
||||
|
||||
secret = get_secret_definition(SECRET_NAME)
|
||||
if not secret.exists():
|
||||
LOG.error("Secret '%s' does not exist.", SECRET_NAME)
|
||||
sys.exit(1)
|
||||
|
||||
secret.reload()
|
||||
if not PROVIDED_KEYS:
|
||||
LOG.info("No fernet keys were provided in the config.")
|
||||
if args.command == 'fernet_rotate':
|
||||
LOG.info("Copying existing fernet keys from secret '%s' to %s.", SECRET_NAME, FERNET_DIR)
|
||||
write_to_files(secret.obj['data'])
|
||||
|
||||
LOG.info("Executing 'keystone-manage %s --keystone-user=keystone --keystone-group=keystone' command.",
|
||||
args.command)
|
||||
subprocess.call(['keystone-manage', args.command, '--keystone-user=keystone', '--keystone-group=keystone'])
|
||||
|
||||
LOG.info("Updating data for '%s' secret.", SECRET_NAME)
|
||||
updated_keys = get_keys_data()
|
||||
secret.obj['data'] = updated_keys
|
||||
secret.update()
|
||||
LOG.info("%s fernet keys have been placed to secret '%s'", len(updated_keys), SECRET_NAME)
|
||||
LOG.debug("Placed keys: %s", updated_keys)
|
||||
LOG.info("Fernet keys %s has been completed", "rotation" if args.command == 'fernet_rotate' else "generation")
|
||||
|
||||
if __name__ == "__main__":
|
||||
set_globals()
|
||||
main()
|
Loading…
Reference in New Issue