Add credential delete hook to keystone chart

This change creates a pre-delete hook to clean out all entries
in the credential table of the keystone database when the
keystone service is deleted. Note that these are not
the typical username/password.[0]

This fixes the issue of leftover credential blobs being saved
in the database that are unable to be decrypted since the
original encryption keys are removed upon deletion of the
keystone service

[0] https://specs.openstack.org/openstack/keystone-specs/specs/keystone/newton/credential-encryption.html

Change-Id: I8adf0878af2f3b880e9194a6cb8d97b58d6895a5
This commit is contained in:
Gage Hugo 2018-12-10 10:59:37 -06:00
parent db87cf6390
commit 3d6f3088a3
4 changed files with 245 additions and 0 deletions

View File

@ -0,0 +1,121 @@
{{/*
Copyright 2019 The Openstack-Helm Authors.
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.
*/}}
#!/usr/bin/python
# Drops db and user for an OpenStack Service:
# Set ROOT_DB_CONNECTION and DB_CONNECTION environment variables to contain
# SQLAlchemy strings for the root connection to the database and the one you
# wish the service to use. Alternatively, you can use an ini formatted config
# at the location specified by OPENSTACK_CONFIG_FILE, and extract the string
# from the key OPENSTACK_CONFIG_DB_KEY, in the section specified by
# OPENSTACK_CONFIG_DB_SECTION.
import os
import sys
import ConfigParser
import logging
from sqlalchemy import create_engine
# Create logger, console handler and formatter
logger = logging.getLogger('OpenStack-Helm DB Drop')
logger.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# Set the formatter and add the handler
ch.setFormatter(formatter)
logger.addHandler(ch)
# Get the connection string for the service db root user
if "ROOT_DB_CONNECTION" in os.environ:
db_connection = os.environ['ROOT_DB_CONNECTION']
logger.info('Got DB root connection')
else:
logger.critical('environment variable ROOT_DB_CONNECTION not set')
sys.exit(1)
# Get the connection string for the service db
if "OPENSTACK_CONFIG_FILE" in os.environ:
os_conf = os.environ['OPENSTACK_CONFIG_FILE']
if "OPENSTACK_CONFIG_DB_SECTION" in os.environ:
os_conf_section = os.environ['OPENSTACK_CONFIG_DB_SECTION']
else:
logger.critical('environment variable OPENSTACK_CONFIG_DB_SECTION not set')
sys.exit(1)
if "OPENSTACK_CONFIG_DB_KEY" in os.environ:
os_conf_key = os.environ['OPENSTACK_CONFIG_DB_KEY']
else:
logger.critical('environment variable OPENSTACK_CONFIG_DB_KEY not set')
sys.exit(1)
try:
config = ConfigParser.RawConfigParser()
logger.info("Using {0} as db config source".format(os_conf))
config.read(os_conf)
logger.info("Trying to load db config from {0}:{1}".format(
os_conf_section, os_conf_key))
user_db_conn = config.get(os_conf_section, os_conf_key)
logger.info("Got config from {0}".format(os_conf))
except:
logger.critical("Tried to load config from {0} but failed.".format(os_conf))
raise
elif "DB_CONNECTION" in os.environ:
user_db_conn = os.environ['DB_CONNECTION']
logger.info('Got config from DB_CONNECTION env var')
else:
logger.critical('Could not get db config, either from config file or env var')
sys.exit(1)
# Root DB engine
try:
root_engine_full = create_engine(db_connection)
root_user = root_engine_full.url.username
root_password = root_engine_full.url.password
drivername = root_engine_full.url.drivername
host = root_engine_full.url.host
port = root_engine_full.url.port
root_engine_url = ''.join([drivername, '://', root_user, ':', root_password, '@', host, ':', str (port)])
root_engine = create_engine(root_engine_url)
connection = root_engine.connect()
connection.close()
logger.info("Tested connection to DB @ {0}:{1} as {2}".format(
host, port, root_user))
except:
logger.critical('Could not connect to database as root user')
raise
# User DB engine
try:
user_engine = create_engine(user_db_conn)
# Get our user data out of the user_engine
database = user_engine.url.database
user = user_engine.url.username
password = user_engine.url.password
logger.info('Got user db config')
except:
logger.critical('Could not get user database config')
raise
# Delete all entries from credential table
try:
cmd = "DELETE FROM credential"
user_engine.execute(cmd)
logger.info('Deleted all entries in credential table')
except:
logger.critical('Failed to clean up credential table in keystone db')
raise
logger.info('Finished DB Management')

View File

@ -35,6 +35,8 @@ data:
{{ tuple $rallyTests | include "helm-toolkit.scripts.rally_test" | indent 4 }} {{ tuple $rallyTests | include "helm-toolkit.scripts.rally_test" | indent 4 }}
ks-user.sh: | ks-user.sh: |
{{- include "helm-toolkit.scripts.keystone_user" . | indent 4 }} {{- include "helm-toolkit.scripts.keystone_user" . | indent 4 }}
cred-clean.py: |
{{ tuple "bin/_cred-clean.py.tpl" . | include "helm-toolkit.utils.template" |indent 4}}
db-init.py: | db-init.py: |
{{- include "helm-toolkit.scripts.db_init" . | indent 4 }} {{- include "helm-toolkit.scripts.db_init" . | indent 4 }}
db-sync.sh: | db-sync.sh: |

View File

@ -0,0 +1,104 @@
{{/*
Copyright 2019 The Openstack-Helm Authors.
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.
*/}}
{{- if .Values.manifests.job_credential_cleanup }}
{{- $envAll := index . -}}
{{- $serviceName := "keystone" -}}
{{- $nodeSelector := index . "nodeSelector" | default ( dict $envAll.Values.labels.job.node_selector_key $envAll.Values.labels.job.node_selector_value ) -}}
{{- $configMapBin := "keystone-bin" -}}
{{- $configMapEtc := "keystone-etc" -}}
{{- $dbToClean := index . "dbToClean" | default ( dict "adminSecret" $envAll.Values.secrets.oslo_db.admin "configFile" (printf "/etc/%s/%s.conf" $serviceName $serviceName ) "logConfigFile" (printf "/etc/%s/logging.conf" $serviceName ) "configDbSection" "database" "configDbKey" "connection" ) -}}
{{ tuple $envAll "credential_cleanup" $serviceName | include "helm-toolkit.snippets.kubernetes_pod_rbac_serviceaccount" }}
---
apiVersion: batch/v1
kind: Job
metadata:
name: "keystone-credential-cleanup"
annotations:
"helm.sh/hook": pre-delete
"helm.sh/hook-delete-policy": hook-succeed
spec:
template:
metadata:
labels:
{{ tuple $envAll $serviceName "credential-cleanup" | include "helm-toolkit.snippets.kubernetes_metadata_labels" | indent 8 }}
spec:
serviceAccountName: {{ $serviceName }}
restartPolicy: OnFailure
nodeSelector:
{{ toYaml $nodeSelector | indent 8 }}
initContainers:
{{ tuple $envAll "credential_cleanup" list | include "helm-toolkit.snippets.kubernetes_entrypoint_init_container" | indent 8 }}
containers:
{{ $dbToCleanType := default "oslo" $dbToClean.inputType }}
- name: {{ printf "%s-%s" $serviceName "credential-cleanup" | quote }}
image: {{ $envAll.Values.images.tags.keystone_credential_cleanup }}
imagePullPolicy: {{ $envAll.Values.images.pull_policy }}
{{ tuple $envAll $envAll.Values.pod.resources.jobs.db_drop | include "helm-toolkit.snippets.kubernetes_resources" | indent 10 }}
env:
- name: ROOT_DB_CONNECTION
valueFrom:
secretKeyRef:
name: {{ $dbToClean.adminSecret | quote }}
key: DB_CONNECTION
{{- if eq $dbToCleanType "oslo" }}
- name: OPENSTACK_CONFIG_FILE
value: {{ $dbToClean.configFile | quote }}
- name: OPENSTACK_CONFIG_DB_SECTION
value: {{ $dbToClean.configDbSection | quote }}
- name: OPENSTACK_CONFIG_DB_KEY
value: {{ $dbToClean.configDbKey | quote }}
{{- end }}
command:
- python
- /tmp/cred-clean.py
volumeMounts:
- name: cred-clean-sh
mountPath: /tmp/cred-clean.py
subPath: cred-clean.py
readOnly: true
{{- if eq $dbToCleanType "oslo" }}
- name: etc-service
mountPath: {{ dir $dbToClean.configFile | quote }}
- name: cred-clean-conf
mountPath: {{ $dbToClean.configFile | quote }}
subPath: {{ base $dbToClean.configFile | quote }}
readOnly: true
- name: cred-clean-conf
mountPath: {{ $dbToClean.logConfigFile | quote }}
subPath: {{ base $dbToClean.logConfigFile | quote }}
readOnly: true
{{- end }}
volumes:
- name: cred-clean-sh
configMap:
name: "keystone-bin"
defaultMode: 0555
{{- $local := dict "configMapBinFirst" true -}}
{{- $dbToCleanType := default "oslo" $dbToClean.inputType }}
{{- if and (eq $dbToCleanType "oslo") $local.configMapBinFirst }}
{{- $_ := set $local "configMapBinFirst" false }}
- name: etc-service
emptyDir: {}
- name: cred-clean-conf
secret:
secretName: "keystone-etc"
defaultMode: 0444
{{- end -}}
{{- end -}}

View File

@ -40,6 +40,7 @@ images:
keystone_fernet_rotate: docker.io/openstackhelm/keystone:ocata keystone_fernet_rotate: docker.io/openstackhelm/keystone:ocata
keystone_credential_setup: docker.io/openstackhelm/keystone:ocata keystone_credential_setup: docker.io/openstackhelm/keystone:ocata
keystone_credential_rotate: docker.io/openstackhelm/keystone:ocata keystone_credential_rotate: docker.io/openstackhelm/keystone:ocata
keystone_credential_cleanup: docker.io/openstackhelm/heat:ocata
keystone_api: docker.io/openstackhelm/keystone:ocata keystone_api: docker.io/openstackhelm/keystone:ocata
keystone_domain_manage: docker.io/openstackhelm/keystone:ocata keystone_domain_manage: docker.io/openstackhelm/keystone:ocata
dep_check: quay.io/stackanetes/kubernetes-entrypoint:v0.3.1 dep_check: quay.io/stackanetes/kubernetes-entrypoint:v0.3.1
@ -113,6 +114,10 @@ dependencies:
jobs: jobs:
- keystone-credential-setup - keystone-credential-setup
credential_setup: null credential_setup: null
credential_cleanup:
services:
- endpoint: internal
service: oslo_db
db_drop: db_drop:
services: services:
- endpoint: internal - endpoint: internal
@ -212,6 +217,11 @@ pod:
keystone_credential_rotate: keystone_credential_rotate:
volumeMounts: volumeMounts:
volumes: volumes:
keystone_credential_cleanup:
init_container: null
keystone_credential_cleanup:
volumeMounts:
volumes:
keystone_domain_manage: keystone_domain_manage:
init_container: null init_container: null
keystone_domain_manage: keystone_domain_manage:
@ -320,6 +330,13 @@ pod:
limits: limits:
memory: "1024Mi" memory: "1024Mi"
cpu: "2000m" cpu: "2000m"
credential_cleanup:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "1024Mi"
cpu: "2000m"
image_repo_sync: image_repo_sync:
requests: requests:
memory: "128Mi" memory: "128Mi"
@ -1245,6 +1262,7 @@ manifests:
deployment_api: true deployment_api: true
ingress_api: true ingress_api: true
job_bootstrap: true job_bootstrap: true
job_credential_cleanup: true
job_credential_setup: true job_credential_setup: true
job_db_init: true job_db_init: true
job_db_sync: true job_db_sync: true