Merge "Barbican: Add support for master KEK rotation"
This commit is contained in:
commit
9bdd4fe96f
@ -14,7 +14,7 @@ apiVersion: v1
|
||||
appVersion: v1.0.0
|
||||
description: OpenStack-Helm Barbican
|
||||
name: barbican
|
||||
version: 0.2.2
|
||||
version: 0.2.3
|
||||
home: https://docs.openstack.org/barbican/latest/
|
||||
icon: https://www.openstack.org/themes/openstack/images/project-mascots/Barbican/OpenStack_Project_Barbican_vertical.png
|
||||
sources:
|
||||
|
@ -17,3 +17,11 @@ limitations under the License.
|
||||
set -ex
|
||||
|
||||
barbican-db-manage upgrade
|
||||
|
||||
{{- $kek := (index (index .Values.conf.barbican "simple_crypto_plugin" | default dict) "kek") | default "" }}
|
||||
{{- $old_kek := index .Values.conf.simple_crypto_kek_rewrap "old_kek" | default ""}}
|
||||
{{- if and (not (empty $old_kek)) (not (empty $kek)) }}
|
||||
set +x
|
||||
echo "Ensuring that project KEKs are wrapped with the target global KEK"
|
||||
/tmp/simple_crypto_kek_rewrap.py --old-kek="$(cat /tmp/old_kek)"
|
||||
{{- end }}
|
||||
|
158
barbican/templates/bin/_simple_crypto_kek_rewrap.py.tpl
Normal file
158
barbican/templates/bin/_simple_crypto_kek_rewrap.py.tpl
Normal file
@ -0,0 +1,158 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import argparse
|
||||
import base64
|
||||
import sys
|
||||
|
||||
from cryptography import fernet
|
||||
from oslo_db.sqlalchemy import session
|
||||
from sqlalchemy import orm
|
||||
from sqlalchemy.orm import scoping
|
||||
|
||||
from barbican.common import utils
|
||||
from barbican.model import models
|
||||
from barbican.plugin.crypto import simple_crypto
|
||||
|
||||
# Use config values from simple_crypto
|
||||
CONF = simple_crypto.CONF
|
||||
|
||||
|
||||
class KekRewrap(object):
|
||||
|
||||
def __init__(self, conf, old_kek):
|
||||
self.dry_run = False
|
||||
self.db_engine = session.create_engine(conf.sql_connection)
|
||||
self._session_creator = scoping.scoped_session(
|
||||
orm.sessionmaker(
|
||||
bind=self.db_engine,
|
||||
autocommit=True
|
||||
)
|
||||
)
|
||||
self.crypto_plugin = simple_crypto.SimpleCryptoPlugin(conf)
|
||||
self.plugin_name = utils.generate_fullname_for(self.crypto_plugin)
|
||||
self.decryptor = fernet.Fernet(old_kek.encode('utf-8'))
|
||||
self.encryptor = fernet.Fernet(self.crypto_plugin.master_kek)
|
||||
|
||||
def rewrap_kek(self, project, kek):
|
||||
with self.db_session.begin():
|
||||
plugin_meta = kek.plugin_meta
|
||||
|
||||
# try to unwrap with the target kek, and if successful, skip
|
||||
try:
|
||||
if self.encryptor.decrypt(plugin_meta.encode('utf-8')):
|
||||
print('Project KEK {} is already wrapped with target KEK, skipping'.format(kek.id))
|
||||
return
|
||||
except fernet.InvalidToken:
|
||||
pass
|
||||
|
||||
# decrypt with the old kek
|
||||
print('Unwrapping Project KEK {}'.format(kek.id))
|
||||
try:
|
||||
decrypted_plugin_meta = self.decryptor.decrypt(plugin_meta.encode('utf-8'))
|
||||
except fernet.InvalidToken:
|
||||
print('Failed to unwrap Project KEK {}'.format(kek.id))
|
||||
raise
|
||||
|
||||
# encrypt with the new kek
|
||||
print('Rewrapping Project KEK {}'.format(kek.id))
|
||||
try:
|
||||
new_plugin_meta = self.encryptor.encrypt(decrypted_plugin_meta).decode('utf-8')
|
||||
except fernet.InvalidToken:
|
||||
print('Failed to wrap Project KEK {}'.format(kek.id))
|
||||
raise
|
||||
|
||||
if self.dry_run:
|
||||
return
|
||||
|
||||
# Update KEK metadata in DB
|
||||
print('Storing updated Project KEK {}'.format(kek.id))
|
||||
kek.plugin_meta = new_plugin_meta
|
||||
|
||||
def get_keks_for_project(self, project):
|
||||
keks = []
|
||||
with self.db_session.begin() as transaction:
|
||||
print('Retrieving KEKs for Project {}'.format(project.external_id))
|
||||
query = transaction.session.query(models.KEKDatum)
|
||||
query = query.filter_by(project_id=project.id)
|
||||
query = query.filter_by(plugin_name=self.plugin_name)
|
||||
|
||||
keks = query.all()
|
||||
|
||||
return keks
|
||||
|
||||
def get_projects(self):
|
||||
print('Retrieving all available projects')
|
||||
|
||||
projects = []
|
||||
with self.db_session.begin() as transaction:
|
||||
projects = transaction.session.query(models.Project).all()
|
||||
|
||||
return projects
|
||||
|
||||
@property
|
||||
def db_session(self):
|
||||
return self._session_creator()
|
||||
|
||||
def execute(self, dry_run=True):
|
||||
self.dry_run = dry_run
|
||||
if self.dry_run:
|
||||
print('-- Running in dry-run mode --')
|
||||
|
||||
projects = self.get_projects()
|
||||
successes = []
|
||||
failures = []
|
||||
|
||||
for project in projects:
|
||||
keks = self.get_keks_for_project(project)
|
||||
for kek in keks:
|
||||
try:
|
||||
self.rewrap_kek(project, kek)
|
||||
successes.append(kek.id)
|
||||
except Exception:
|
||||
failures.append(kek.id)
|
||||
|
||||
if successes:
|
||||
print('Sucessfully processed the following KEKs:')
|
||||
print('\n'.join(successes))
|
||||
|
||||
if failures:
|
||||
print('Failed to rewrap the following KEKs:')
|
||||
print('\n'.join(failures))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main():
|
||||
script_desc = 'Utility to re-wrap Project KEKs after rotating the global KEK.'
|
||||
|
||||
parser = argparse.ArgumentParser(description=script_desc)
|
||||
parser.add_argument(
|
||||
'--dry-run',
|
||||
action='store_true',
|
||||
help='Displays changes that will be made (Non-destructive)'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--old-kek',
|
||||
default='dGhpcnR5X3R3b19ieXRlX2tleWJsYWhibGFoYmxhaGg=',
|
||||
help='Old key encryption key previously used by Simple Crypto Plugin. '
|
||||
'(32 bytes, base64-encoded)'
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
rewrapper = KekRewrap(CONF, args.old_kek)
|
||||
rewrapper.execute(args.dry_run)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -46,4 +46,6 @@ data:
|
||||
{{- include "helm-toolkit.scripts.keystone_user" . | indent 4 }}
|
||||
rabbit-init.sh: |
|
||||
{{- include "helm-toolkit.scripts.rabbit_init" . | indent 4 }}
|
||||
simple_crypto_kek_rewrap.py: |
|
||||
{{ tuple "bin/_simple_crypto_kek_rewrap.py.tpl" . | include "helm-toolkit.utils.template" | indent 4 }}
|
||||
{{- end }}
|
||||
|
@ -95,4 +95,5 @@ data:
|
||||
api_audit_map.conf: {{ include "helm-toolkit.utils.to_ini" .Values.conf.audit_map | b64enc }}
|
||||
policy.yaml: {{ toYaml .Values.conf.policy | b64enc }}
|
||||
barbican-api.ini: {{ include "helm-toolkit.utils.to_ini" .Values.conf.barbican_api | b64enc }}
|
||||
old_kek: {{ index .Values.conf.simple_crypto_kek_rewrap "old_kek" | default "" | b64enc | quote }}
|
||||
{{- end }}
|
||||
|
@ -19,7 +19,11 @@ helm.sh/hook-weight: "-4"
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- $podVolMounts := .Values.pod.mounts.barbican_db_sync.barbican_db_sync.volumeMounts | default list }}
|
||||
{{- $podVolMounts = append $podVolMounts (dict "name" "db-sync-sh" "mountPath" "/tmp/simple_crypto_kek_rewrap.py" "subPath" "simple_crypto_kek_rewrap.py" "readOnly" true) }}
|
||||
{{- $podVolMounts = append $podVolMounts (dict "name" "db-sync-conf" "mountPath" "/tmp/old_kek" "subPath" "old_kek" "readOnly" true) }}
|
||||
|
||||
{{- if .Values.manifests.job_db_sync }}
|
||||
{{- $dbSyncJob := dict "envAll" . "serviceName" "barbican" "podVolMounts" .Values.pod.mounts.barbican_db_sync.barbican_db_sync.volumeMounts "podVols" .Values.pod.mounts.barbican_db_sync.barbican_db_sync.volumes "jobAnnotations" (include "metadata.annotations.job.db_sync" . | fromYaml) -}}
|
||||
{{- $dbSyncJob := dict "envAll" . "serviceName" "barbican" "podVolMounts" $podVolMounts "podVols" .Values.pod.mounts.barbican_db_sync.barbican_db_sync.volumes "jobAnnotations" (include "metadata.annotations.job.db_sync" . | fromYaml) -}}
|
||||
{{ $dbSyncJob | include "helm-toolkit.manifests.job_db_sync" }}
|
||||
{{- end }}
|
||||
|
@ -470,6 +470,44 @@ conf:
|
||||
bind_port: null
|
||||
oslo_policy:
|
||||
policy_file: /etc/barbican/policy.yaml
|
||||
# When using the simple_crypto_plugin, a kek must be provided as:
|
||||
# .conf.barbican.simple_crypto_plugin.kek
|
||||
# If no kek is provided, barbican will use a well-known default.
|
||||
# If upgrading the chart with a new kek, the old kek must be provided as:
|
||||
# .conf.simple_crypto_plugin_rewrap.old_kek
|
||||
# Please refer to the .conf.simple_crypto_key_rewrap section below.
|
||||
# The barbican defaults are included here as a reference:
|
||||
# secretstore:
|
||||
# enabled_secretstore_plugins:
|
||||
# - store_crypto
|
||||
# crypto:
|
||||
# enabled_crypto_plugins:
|
||||
# - simple_crypto
|
||||
# simple_crypto_plugin:
|
||||
# # The kek should be a 32-byte value which is base64 encoded.
|
||||
# kek: "dGhpcnR5X3R3b19ieXRlX2tleWJsYWhibGFoYmxhaGg="
|
||||
# KEK rotation for the simple_crypto plugin
|
||||
simple_crypto_kek_rewrap:
|
||||
|
||||
# To allow for chart upgrades when modifying the Key Encryption Key, the
|
||||
# db-sync job can rewrap the existing project keys with the new kek, leaving
|
||||
# each secret’s encrypted data unchanged.
|
||||
|
||||
# This feature is enabled automatically, if a kek is specified at:
|
||||
# .conf.barbican.simple_crypto_plugin.kek
|
||||
# and the previous kek is also specified at:
|
||||
# .conf.simple_crypto_kek_rewrap.old_kek
|
||||
|
||||
# The project keys are decrypted with 'old_kek' and re-encrypted with the
|
||||
# target kek (as defined in barbican.conf).
|
||||
# This resembles the lightweight rotation described here, which was never
|
||||
# implemented for the simple crypto plugin:
|
||||
# https://specs.openstack.org/openstack/barbican-specs/specs/liberty/add-crypto-mkek-rotation-support-lightweight.html
|
||||
|
||||
# The KEK value "dGhpcnR5X3R3b19ieXRlX2tleWJsYWhibGFoYmxhaGg=" matches the
|
||||
# plugin default, and is retained here for convenience, in case the chart was
|
||||
# previously installed without explicitly specifying a kek.
|
||||
old_kek: "dGhpcnR5X3R3b19ieXRlX2tleWJsYWhibGFoYmxhaGg="
|
||||
logging:
|
||||
loggers:
|
||||
keys:
|
||||
|
@ -6,3 +6,4 @@ barbican:
|
||||
- 0.2.0 Remove support for releases before T
|
||||
- 0.2.1 Use policies in yaml format
|
||||
- 0.2.2 Add helm hook conditional
|
||||
- 0.2.3 Add support for master kek rotation
|
||||
|
Loading…
Reference in New Issue
Block a user