Deploy Barbican
OpenStack Barbican is the dedicated security service within the OpenStack ecosystem, focused on the secure storage, management, and provisioning of sensitive data such as encryption keys, certificates, and passwords. Barbican plays a crucial role in enhancing the security posture of cloud environments by providing a centralized and controlled repository for cryptographic secrets, ensuring that sensitive information is protected and accessible only to authorized services and users. It integrates seamlessly with other OpenStack services to offer encryption and secure key management capabilities, which are essential for maintaining data confidentiality and integrity. In this document, we will explore the deployment of OpenStack Barbican using Genestack. With Genestack, the deployment of Barbican is optimized, ensuring that cloud infrastructures are equipped with strong and scalable security measures for managing critical secrets.
Create secrets
Information about the secrets used
Service secrets are managed idempotently by this service's install script. The installer creates any missing Kubernetes secrets and reuses existing values.
Setup Barbican Overrides
When deploying barbican, it is important to provide the necessary configuration values to ensure that the service is properly
configured and integrated with other OpenStack services. The /etc/genestack/helm-configs/barbican/barbican-helm-overrides.yaml
file contains the necessary configuration values for Barbican, including database connection details, RabbitMQ credentials, and other
service-specific settings. By providing these values, you can customize the deployment of Barbican to meet your specific requirements
and ensure that the service operates correctly within your OpenStack environment.
Gazpacho (2026.1) / OpenStack 2026.1
Barbican is validated here against the OpenStack 2026.1 stream.
This update does not include direct changes to barbican-helm-overrides.yaml.
Set the host_href value
The host_href value should be set to the public endpoint of the Barbican service. This value is used by other OpenStack services and public consumers to communicate with Barbican and should be accessible from all OpenStack services.
Run the package deployment
Run the Barbican deployment Script /opt/genestack/bin/install-barbican.sh
#!/bin/bash
# Description: Fetches the version for SERVICE_NAME_DEFAULT from the specified
# YAML file and executes a helm upgrade/install command with dynamic values files.
# Disable SC2124 (unused array), SC2145 (array expansion issue), SC2294 (eval)
# shellcheck disable=SC2124,SC2145,SC2294
# Service
# The service name is used for both the release name and the chart name.
SERVICE_NAME_DEFAULT="barbican"
SERVICE_NAMESPACE="openstack"
# Helm
HELM_REPO_NAME_DEFAULT="openstack-helm"
HELM_REPO_URL_DEFAULT="https://tarballs.opendev.org/openstack/openstack-helm"
# Base directories provided by the environment
GENESTACK_BASE_DIR="${GENESTACK_BASE_DIR:-/opt/genestack}"
GENESTACK_OVERRIDES_DIR="${GENESTACK_OVERRIDES_DIR:-/etc/genestack}"
# Common secret helpers. Missing secrets are generated in Kubernetes and
# existing secrets are never overwritten.
# shellcheck source=helpers.sh
source "${GENESTACK_BASE_DIR}/bin/helpers.sh"
trap cleanup_tmp EXIT
# Define service-specific override directories based on the framework
SERVICE_BASE_OVERRIDES="${GENESTACK_BASE_DIR}/base-helm-configs/${SERVICE_NAME_DEFAULT}"
SERVICE_CUSTOM_OVERRIDES="${GENESTACK_OVERRIDES_DIR}/helm-configs/${SERVICE_NAME_DEFAULT}"
# Define the Global Overrides directory used in the original script
GLOBAL_OVERRIDES_DIR="${GENESTACK_OVERRIDES_DIR}/helm-configs/global_overrides"
# Read the desired chart version from VERSION_FILE
VERSION_FILE="${GENESTACK_OVERRIDES_DIR}/helm-chart-versions.yaml"
if [ ! -f "$VERSION_FILE" ]; then
echo "Error: helm-chart-versions.yaml not found at $VERSION_FILE" >&2
exit 1
fi
# Extract version dynamically using the SERVICE_NAME_DEFAULT variable
SERVICE_VERSION=$(grep "^[[:space:]]*${SERVICE_NAME_DEFAULT}:" "$VERSION_FILE" | sed "s/.*${SERVICE_NAME_DEFAULT}: *//")
if [ -z "$SERVICE_VERSION" ]; then
echo "Error: Could not extract version for '$SERVICE_NAME_DEFAULT' from $VERSION_FILE" >&2
exit 1
fi
echo "Found version for $SERVICE_NAME_DEFAULT: $SERVICE_VERSION"
# Load chart metadata from custom override YAML if defined
for yaml_file in "${SERVICE_CUSTOM_OVERRIDES}"/*.yaml; do
if [ -f "$yaml_file" ]; then
HELM_REPO_URL=$(yq eval '.chart.repo_url // ""' "$yaml_file")
HELM_REPO_NAME=$(yq eval '.chart.repo_name // ""' "$yaml_file")
SERVICE_NAME=$(yq eval '.chart.service_name // ""' "$yaml_file")
break # use the first match and stop
fi
done
# Fallback to defaults if variables not set
: "${HELM_REPO_URL:=$HELM_REPO_URL_DEFAULT}"
: "${HELM_REPO_NAME:=$HELM_REPO_NAME_DEFAULT}"
: "${SERVICE_NAME:=$SERVICE_NAME_DEFAULT}"
# Determine Helm chart path
if [[ "$HELM_REPO_URL" == oci://* ]]; then
# OCI registry path
HELM_CHART_PATH="$HELM_REPO_URL/$HELM_REPO_NAME/$SERVICE_NAME"
else
# --- Helm Repository and Execution ---
helm repo add --force-update "$HELM_REPO_NAME" "$HELM_REPO_URL" 2>/dev/null || true
helm repo update
HELM_CHART_PATH="$HELM_REPO_NAME/$SERVICE_NAME"
fi
# Debug output
echo "[DEBUG] HELM_REPO_URL=$HELM_REPO_URL"
echo "[DEBUG] HELM_REPO_NAME=$HELM_REPO_NAME"
echo "[DEBUG] SERVICE_NAME=$SERVICE_NAME"
echo "[DEBUG] HELM_CHART_PATH=$HELM_CHART_PATH"
# Prepare an array to collect -f arguments
overrides_args=()
# Include all YAML files from the BASE configuration directory
# NOTE: Files in this directory are included first.
if [[ -d "$SERVICE_BASE_OVERRIDES" ]]; then
echo "Including base overrides from directory: $SERVICE_BASE_OVERRIDES"
for file in "$SERVICE_BASE_OVERRIDES"/*.yaml; do
# Check that there is at least one match
if [[ -e "$file" ]]; then
echo " - $file"
overrides_args+=("-f" "$file")
fi
done
else
echo "Warning: Base override directory not found: $SERVICE_BASE_OVERRIDES"
fi
# Include all YAML files from the GLOBAL configuration directory
# NOTE: Files here override base settings and are applied before service-specific ones.
if [[ -d "$GLOBAL_OVERRIDES_DIR" ]]; then
echo "Including global overrides from directory: $GLOBAL_OVERRIDES_DIR"
for file in "$GLOBAL_OVERRIDES_DIR"/*.yaml; do
if [[ -e "$file" ]]; then
echo " - $file"
overrides_args+=("-f" "$file")
fi
done
else
echo "Warning: Global override directory not found: $GLOBAL_OVERRIDES_DIR"
fi
# Include all YAML files from the custom SERVICE configuration directory
# NOTE: Files here have the highest precedence.
if [[ -d "$SERVICE_CUSTOM_OVERRIDES" ]]; then
echo "Including overrides from service config directory:"
for file in "$SERVICE_CUSTOM_OVERRIDES"/*.yaml; do
if [[ -e "$file" ]]; then
echo " - $file"
overrides_args+=("-f" "$file")
fi
done
else
echo "Warning: Service config directory not found: $SERVICE_CUSTOM_OVERRIDES"
fi
echo
# Collect all --set arguments, executing commands and quoting safely
# Collect secret-backed --set arguments from bin/services/${SERVICE_NAME_DEFAULT}.yaml.
collect_service_secret_set_args "$SERVICE_NAME_DEFAULT"
set_args=("${SECRET_HELM_SET_ARGS[@]}")
# SoftHSM p11 is the default in base helm. Treat it as enabled when any
# values file (base, global, or site) has both p11_crypto and libsofthsm2,
# or when a hyperconverged/lab flag is set.
override_file="${SERVICE_CUSTOM_OVERRIDES}/barbican-helm-overrides.yaml"
SOFTHSM_P11=false
for ((i = 0; i < ${#overrides_args[@]}; i++)); do
if [[ "${overrides_args[$i]}" == "-f" ]]; then
f="${overrides_args[$((i + 1))]}"
if [[ -f "${f}" ]] \
&& grep -q "p11_crypto" "${f}" 2>/dev/null \
&& grep -q "libsofthsm2" "${f}" 2>/dev/null; then
SOFTHSM_P11=true
break
fi
fi
done
if [[ "${BARBICAN_HSM_ENABLED:-false}" == "true" ]] || [[ "${HYPERCONVERGED_BARBICAN_HSM:-false}" == "true" ]]; then
SOFTHSM_P11=true
# Generate a SoftHSM overlay only when no site file exists. Never rewrite
# an existing environment override (region, images, policy would be lost).
if [[ ! -f "${override_file}" ]]; then
echo "HSM enabled and ${override_file} is missing. Generating SoftHSM overlay..."
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=/dev/null
source "${SCRIPT_DIR}/../scripts/lib/hyperconverged-common.sh"
writeServiceHelmOverrides "${GENESTACK_OVERRIDES_DIR}/helm-configs"
fi
fi
# Ensure barbican-hsm-credentials exists. The install path creates this secret
# once and leaves it alone on later upgrades.
if [[ "${SOFTHSM_P11}" == "true" ]]; then
existing_pin="$(secret_get "$SERVICE_NAMESPACE" barbican-hsm-credentials pin)" || true
if [[ -n "${existing_pin}" ]]; then
echo "barbican-hsm-credentials already present — leaving PIN unchanged"
else
echo "Creating barbican-hsm-credentials (missing on this brownfield cluster)"
hsm_pin="$(secret_generate 32)"
secret_ensure "$SERVICE_NAMESPACE" barbican-hsm-credentials pin "$hsm_pin"
unset hsm_pin
fi
unset existing_pin
fi
# PKCS#11 SoftHSM2 PIN Injection
hsm_pin="$(secret_get "$SERVICE_NAMESPACE" barbican-hsm-credentials pin)" || true
if [[ -n "${hsm_pin}" ]]; then
echo "HSM credentials found - injecting p11_crypto_plugin.login"
set_args+=(
--set "conf.barbican.p11_crypto_plugin.login=${hsm_pin}"
)
fi
unset hsm_pin
# =============================================================================
# Barbican simple_crypto master KEK
#
# Gazpacho barbican has no built-in default kek: with simple_crypto enabled and
# no kek rendered into barbican.conf, barbican-api crashloops with
# "SimpleCrypto KEK is undefined". A kek set in an override file is deployed as
# is. Otherwise the source of truth is the Kubernetes Secret
# barbican-simple-crypto-kek, managed by this installer:
# kek 44-char Fernet key -> [simple_crypto_plugin] kek
# old_keks comma-separated history -> db-sync rewrap old_kek
#
# The chart's db-sync job rewraps every simple_crypto project KEK one-way from
# old_kek onto the rendered kek; a row it cannot unwrap fails the job, which
# barbican-api waits on (an outage, not data loss). Rotations of a
# Secret-managed kek are staged with scripts/rotate-barbican-kek.py, which this
# script never calls.
#
# PROLOGUE
# override_kek = conf.barbican.simple_crypto_plugin.kek from the -f files,
# last file wins
# secret_kek = read_secret(barbican-simple-crypto-kek, kek)
#
# CASE 1 override_kek set -> deploy as is: the -f files carry the
# kek and any old_kek; nothing is
# injected, no Secret is written
# (NOTICE if a Secret also exists)
#
# CASE 2 secret_kek set -> abort if it fails kek_format_ok
# -> GUARD + INJECT
#
# CASE 3 no Secret, deployed_kek -> adopt the deployed kek from
# barbican-etc into the Secret before
# any random generation is considered
#
# CASE 4 no Secret, no deployed_kek
# -> kubectl apply Secret
# {kek: 32 random bytes, old_keks: ""}
# -> INJECT (nothing to rewrap, no GUARD)
#
# GUARD secret_old = read_secret(..., old_keks)
# deployed_kek = kek_deployed(): the first "kek =" under
# [simple_crypto_plugin] in the rendered barbican.conf;
# no kek line: the upstream default; no release: empty
# no release -> deploy (nothing to compare)
# secret_kek == deployed -> deploy (rewrap is a no-op)
# deployed in secret_old -> deploy with WARNING: staged rotation,
# one-way rewrap, back up the DB first
# otherwise -> abort (rewrap could not succeed)
#
# INJECT
# --set-string conf.barbican.simple_crypto_plugin.kek=secret_kek
# --set-string conf.simple_crypto_kek_rewrap.old_kek=secret_old
# =============================================================================
KEK_SECRET="barbican-simple-crypto-kek"
# Upstream default kek that every pre-Gazpacho deploy without an explicit kek
# ran on. Public knowledge (OpenStack docs, OSH chart values); derived at
# runtime so no key-shaped blob sits in the repo.
KEK_WELL_KNOWN="$(printf '%s' 'thirty_two_byte_keyblahblahblahh' | base64 | tr -d '\n')"
# Decoded data key of a Secret in the service namespace; empty when absent.
read_secret() {
secret_get "$SERVICE_NAMESPACE" "$1" "$2" 2>/dev/null || true
}
# 44 chars that base64-decode to exactly 32 bytes: a Fernet key.
kek_format_ok() {
[[ ${#1} -eq 44 ]] && (( $(printf '%s' "$1" | tr -- '-_' '+/' | base64 -d 2>/dev/null | wc -c) == 32 ))
}
write_kek_secret() {
local kek="$1"
local old_keks="${2:-}"
kubectl --namespace "$SERVICE_NAMESPACE" apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
name: ${KEK_SECRET}
namespace: ${SERVICE_NAMESPACE}
type: Opaque
data:
kek: "$(printf '%s' "$kek" | base64 | tr -d '\n')"
old_keks: "$(printf '%s' "$old_keks" | base64 | tr -d '\n')"
EOF
}
warn_default_kek() {
local source="$1"
cat >&2 <<EOF
################################################################################
# #
# WARNING: BARBICAN IS USING THE WELL-KNOWN DEFAULT SIMPLE_CRYPTO KEK #
# #
# Source: ${source}
# #
# This key is public chart/default material. Any secret encrypted with #
# simple_crypto under this KEK should be treated as protected by a default #
# password. Plan a Barbican KEK rotation with a database backup and use: #
# #
# ${GENESTACK_BASE_DIR}/scripts/rotate-barbican-kek.py --stage #
# #
# Do not delete or randomly replace the KEK on a brownfield cluster; that can #
# strand existing Barbican project keys. #
# #
################################################################################
EOF
}
# The kek the running release renders into barbican.conf: the first "kek ="
# line under [simple_crypto_plugin] in the barbican-etc Secret (the rule the
# rotation tool applies too, so a kek option in another section is never
# mistaken for it). Prints nothing when there is no release. A release with no
# kek line is a pre-Gazpacho deploy on the implicit upstream default, so that
# default is what it prints then.
kek_deployed() {
local conf section line
conf="$(read_secret barbican-etc 'barbican\.conf')"
[[ -n "$conf" ]] || return 0
# the [simple_crypto_plugin] section only: from its header up to the next header
section="$(sed -n '/^\[simple_crypto_plugin\]/,/^\[/p' <<< "$conf")"
# the first kek line; when there are several, the first one encrypts
line="$(grep -m1 -E '^[[:space:]]*kek[[:space:]]*=' <<< "$section")"
line="${line#*=}" # drop "kek =", keeping the key's own trailing =
line="$(sed "s/[[:space:]\"']//g" <<< "$line")" # strip quotes and whitespace
if [[ -z "$line" ]]; then
printf '%s' "$KEK_WELL_KNOWN"
return 0
fi
printf '%s' "$line"
}
# kek set in an override file: the last file wins (helm precedence). Only its
# presence matters here; the -f files deliver the value to helm as is.
override_kek=""
for f in "${overrides_args[@]}"; do
[[ "$f" == "-f" ]] && continue
v="$(yq eval '.conf.barbican.simple_crypto_plugin.kek // ""' "$f" 2>/dev/null | head -n1)"
[[ -n "$v" && "$v" != "null" ]] && override_kek="$v"
done
secret_kek="$(read_secret "$KEK_SECRET" kek)"
if [[ -n "$override_kek" ]]; then
# Case 1: the override files carry the kek (and any old_kek).
echo "conf.barbican.simple_crypto_plugin.kek is set in the override files: deploying it as is."
if [[ "$override_kek" == "$KEK_WELL_KNOWN" ]]; then
warn_default_kek "override files: conf.barbican.simple_crypto_plugin.kek"
fi
if [[ -n "$secret_kek" ]]; then
echo "NOTICE: ${KEK_SECRET} exists but is ignored while the override files set the kek."
fi
elif [[ -n "$secret_kek" ]]; then
# Case 2: the Secret is the kek. Guard the db-sync rewrap before injecting it.
if ! kek_format_ok "$secret_kek"; then
echo "ERROR: ${KEK_SECRET} holds a value that is not a 44-char Fernet key; refusing to deploy" >&2
exit 1
fi
if [[ "$secret_kek" == "$KEK_WELL_KNOWN" ]]; then
warn_default_kek "${SERVICE_NAMESPACE}/${KEK_SECRET}:kek"
fi
secret_old="$(read_secret "$KEK_SECRET" old_keks)"
deployed_kek="$(kek_deployed)"
if [[ -z "$deployed_kek" ]]; then
echo "no barbican release yet: injecting the kek from ${KEK_SECRET}."
elif [[ "$secret_kek" == "$deployed_kek" ]]; then
echo "Secret kek matches the deployed kek; the db-sync rewrap is a no-op."
elif [[ ",${secret_old}," == *",${deployed_kek},"* ]]; then
# A staged rotation. rotate-barbican-kek.py --stage records the deployed
# kek in old_keks, and old_keks is exactly what is injected below, so the
# rewrap's decryptor set never depends on a chart default.
echo "WARNING: Secret kek differs from the deployed kek: this deploy performs a ONE-WAY rewrap of"
echo " every simple_crypto project KEK during db-sync. Back up the barbican DB first."
echo " Afterwards run: ${GENESTACK_BASE_DIR}/scripts/rotate-barbican-kek.py --validate deployed"
echo " and confirm the db-sync job logs show zero rewrap failures."
else
echo "ERROR: Secret kek differs from the deployed kek, and the deployed kek is not in" >&2
echo " ${KEK_SECRET}/old_keks, so the db-sync rewrap could not unwrap the existing project" >&2
echo " keys and barbican would not start. Not deploying. Stage rotations only with" >&2
echo " ${GENESTACK_BASE_DIR}/scripts/rotate-barbican-kek.py --stage (it records the deployed" >&2
echo " kek in old_keks), or delete the Secret deliberately and re-adopt the deployed kek with" >&2
echo " ${GENESTACK_BASE_DIR}/scripts/rotate-barbican-kek.py --adopt before re-running this install." >&2
exit 1
fi
else
# No Secret and no override: first preserve any kek already rendered into
# the deployed barbican-etc secret. Only a deployment with no recoverable
# deployed kek can fall through to fresh generation.
deployed_kek="$(kek_deployed)"
if [[ -n "$deployed_kek" ]]; then
if ! kek_format_ok "$deployed_kek"; then
echo "ERROR: ${KEK_SECRET} is absent and barbican-etc contains an invalid simple_crypto kek;" >&2
echo " refusing to replace it with a random value. Fix or adopt the deployed kek manually." >&2
exit 1
fi
if [[ "$deployed_kek" == "$KEK_WELL_KNOWN" ]]; then
warn_default_kek "${SERVICE_NAMESPACE}/barbican-etc:barbican.conf"
fi
echo "${KEK_SECRET} is absent; adopting the deployed simple_crypto kek from barbican-etc."
write_kek_secret "$deployed_kek" ""
secret_kek="$(read_secret "$KEK_SECRET" kek)"
secret_old=""
if [[ "$secret_kek" != "$deployed_kek" ]]; then
echo "ERROR: ${KEK_SECRET} did not persist the deployed kek read from barbican-etc" >&2
exit 1
fi
else
# No Secret, no override, and no deployed barbican-etc kek. The installer
# has no existing KEK source to preserve, so create the initial managed KEK.
echo "${KEK_SECRET} is absent and no deployed simple_crypto kek was found: generating a fresh kek."
# 32 random bytes, urlsafe base64: the Fernet key format. The Secret is
# written over stdin so the kek never appears in argv or ps.
new_kek="$(head -c 32 /dev/urandom | base64 | tr -d '\n' | tr '+/' '-_')"
write_kek_secret "$new_kek" ""
unset new_kek
# Read it back: only a kek that is actually stored gets injected.
secret_kek="$(read_secret "$KEK_SECRET" kek)"
secret_old=""
if ! kek_format_ok "$secret_kek"; then
echo "ERROR: ${KEK_SECRET} still holds no valid kek after generating one" >&2
exit 1
fi
echo "fresh kek: the db-sync rewrap has no project keys to process."
fi
fi
# Inject (cases 2, 3, and 4).
if [[ -z "$override_kek" ]]; then
set_args+=(--set-string "conf.barbican.simple_crypto_plugin.kek=${secret_kek}")
if [[ -n "$secret_old" ]]; then
# a comma is --set list syntax; escape it so the whole history survives as one string
set_args+=(--set-string "conf.simple_crypto_kek_rewrap.old_kek=${secret_old//,/\\,}")
fi
fi
helm_command=(
helm upgrade --install "$SERVICE_NAME_DEFAULT" "$HELM_CHART_PATH"
--version "${SERVICE_VERSION}"
--namespace="$SERVICE_NAMESPACE"
--timeout 120m
--create-namespace
"${overrides_args[@]}"
"${set_args[@]}"
# Post-renderer configuration
--post-renderer "$GENESTACK_OVERRIDES_DIR/kustomize/kustomize.sh"
--post-renderer-args "$SERVICE_NAME_DEFAULT/overlay"
"$@"
)
echo "Executing Helm command (arguments are quoted safely):"
printf '%q ' "${helm_command[@]}"
echo
# Execute the command directly from the array
"${helm_command[@]}"
# Post-Install SoftHSM2 Key Initialization (token, MKEK, HMAC).
# Runs when the site override already enables SoftHSM p11, or when the
# hyperconverged/lab HSM flags are set. No extra env var required.
if [[ "${SOFTHSM_P11}" == "true" ]]; then
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if ! declare -f initBarbicanHSMKeys >/dev/null 2>&1; then
common_sh="${SCRIPT_DIR}/../scripts/lib/hyperconverged-common.sh"
if [[ -f "${common_sh}" ]]; then
# shellcheck source=/dev/null
source "${common_sh}" >/dev/null 2>&1 || true
fi
fi
if declare -f initBarbicanHSMKeys >/dev/null 2>&1; then
initBarbicanHSMKeys
else
echo "ERROR: initBarbicanHSMKeys not found; SoftHSM keys were not initialized"
exit 1
fi
fi
Note
For Gazpacho validation, DB credentials are injected at install time from Kubernetes secrets in
bin/install-barbican.sh (for example endpoints.oslo_db.auth.admin.password and
endpoints.oslo_db.auth.barbican.password).
Tip
In other cases such as a multi-region deployment you may want to view the Multi-Region Support guide to for a workflow solution.
Simple crypto master KEK
Barbican's simple_crypto plugin wraps every project key with a master key-encryption-key (KEK). Since Gazpacho there is
no built-in default: if no KEK is rendered into barbican.conf, barbican-api fails to start with SimpleCrypto KEK is
undefined. A KEK set in an override file (conf.barbican.simple_crypto_plugin.kek) always takes precedence and is
deployed as is: install-barbican.sh injects nothing and writes no Secret. Otherwise Genestack keeps the KEK in the
barbican-simple-crypto-kek Kubernetes Secret, which install-barbican.sh creates on a new deployment and injects on
every deploy together with the old_keks rotation history.
What install-barbican.sh does when neither an override file nor the Secret supplies a KEK
- If a running deployment already rendered a KEK into the
barbican-etcSecret, the installer adopts that KEK intobarbican-simple-crypto-kekbefore any random generation is considered. - If no override, managed Secret, or deployed
barbican-etcKEK exists, the installer treats the deployment as a first install and generates a fresh KEK into the Secret.
When the Secret holds a KEK that differs from the deployed one, the deploy only proceeds if the deployed KEK is listed
in the Secret's old_keks, which --stage always records. Otherwise the script refuses, because the db-sync rewrap
could not succeed. A KEK from an override file gets no such check: on that path the chart's db-sync job is the only
safeguard, and it fails the deploy rather than losing data when the KEK cannot unwrap the existing project keys.
Rotate the KEK
Environments that never set a KEK have been running on Barbican's publicly documented default key, and rotating retires it.
A rotation of a Secret-managed KEK is staged with /opt/genestack/scripts/rotate-barbican-kek.py and executed by the chart's
db-sync job on the next deploy, which rewraps every project key one-way. install-barbican.sh never calls the tool. Without
flags the tool is a read-only plan, and it never prints key material. Genestack injects a single kek and the previous
keys as the chart's comma-separated old_kek, so the rotation is the db-sync job's one-way rewrap, which is why the backup
below is not optional. Never set kek as a plain YAML list: the chart renders it as one comma-joined value that Fernet
rejects.
Back up the Barbican database before deploying a staged rotation
Octavia certificates and Cinder volume encryption keys depend on Barbican secrets staying decryptable.
/opt/genestack/scripts/rotate-barbican-kek.py # plan: validates the deployed KEK, writes nothing
/opt/genestack/scripts/rotate-barbican-kek.py --stage # writes the new KEK and history into the Secret
# back up the barbican database here: the next step rewraps every project key one-way
/opt/genestack/bin/install-barbican.sh # db-sync rewraps every project KEK
/opt/genestack/scripts/rotate-barbican-kek.py --validate deployed # must pass; db-sync logs must show zero failures
Move an existing environment under Secret management without rotating
--adopt validates the currently deployed KEK against the database from inside a ready barbican-api pod and
stores it in the Secret. (Note: barbican-api must be running; if a previous deploy crashlooped, helm rollback
to the last good revision first). The installer can also adopt the deployed KEK directly from barbican-etc when
no managed Secret exists, but the rotation tool remains the safer operator workflow when you want database
validation before changing management mode. If a kek line is set in your override files, remove it (since an
override always takes precedence over the Secret), then run install-barbican.sh: the Secret's KEK matches the
deployed one, so nothing is rewrapped.