shdctl secret commands
Generate and deploy Kubernetes Secret manifests from a secrets.import.yaml input, and recover that input from a cluster
読み終わるまでの所要時間 13 分最終更新 2日前
Use the command group to render Kubernetes manifests from your import file, apply them to your cluster, and recover that import file from a cluster.
secretSecretGenerate secrets
Create Kubernetes secret YAML files from your secret definitions. For the import file format, refer to the annotated example import file.
Generate the secrets from your import file without prompting:
shdctl secret generate --import secrets.import.yaml --use-defaults
Parameters:
- : Path to the secrets import file, in the import file format. Any value in the file the release's schema does not ask for is not read, and each one is warned about, naming the file and the entry — a secret the schema does not define, a key that secret does not declare, or a key naming a field the schema derives from a default. Read those before deploying: where the schema generates the field you meant to supply, a fresh random value is generated in place of yours.
--import - : Use default values or auto-generate values without prompts (non-interactive mode). If a required field has no default or auto-generate option, the tool writes
--use-defaultsand logs a warning so you can update it before deployment. WithTBD, a placeholder is saved like any other value, and runs that read that file back reuse it without repeating the warning: replace it in the file.--persist - : Output file path (defaults to
--output). An existing file is replaced on every runsecrets.yaml - : Path to the extracted release directory (defaults to
--extracted-release)./extracted-release - : Write only the secret with this name — one this release and manifest produce, or the image pull secret. The secrets derive from one another, so every generated value has to come from your input: pass
--namewith the file--importrebuilds from the cluster, or yourshdctl secret exportfile. A run that would mint one afresh is refused, naming it; so is an unknown name, listing the ones that would work, and the pull secret's name when that secret would be skipped. Nothing is written in any of these cases--persist - : Remove the output file before generating, so a run that fails does not leave the previous one behind (default: false). Nothing else in its directory is touched
--clean-output - : Skip the shdctl version compatibility check against the release package
--skip-version-check - : Save the values of this run back to an import-format file at
--persist <path>for reuse on subsequent runs — every value you supplied or shdctl generated, not the ones derived from them. On re-runs, the persisted file is auto-loaded and existing values are reused without regeneration — but only when no<path>is given: with--import, that file supplies the values and--importis only written. The path is required (no implicit default).<path>
Example:
shdctl secret generate --import secrets.import.yaml --use-defaults --output ./generated-secrets.yaml
Keep generated values stable across runs. Any value your import file does not carry is generated afresh on every run: a database password, an encryption key, the storage abstraction's RSA key. Deploying a regenerated value over the one a running service holds breaks that service. So once a deployment exists, generate from a file that carries every value — the one writes, or the one rebuilds from the cluster. When an upgrade adds a secret, pass on the run whose output you deploy, so the file records the value the cluster gets: a later run would mint a different one. If you missed it, rebuilds the file from the cluster.
--persistshdctl secret export--persistshdctl secret exportObject store credentials
With in your manifest, the object store's credentials are yours to supply — prompts for them instead of generating them, because a generated key could never authenticate against your store:
configuration.objectStore.provider: s3secret generateEnter the access key ID for your S3-compatible object store (needs read, write and delete on every bucket named in configuration.objectStore.buckets, including studioAssets):Enter the secret access key for your S3-compatible object store (needs read, write and delete on every bucket named in configuration.objectStore.buckets, including studioAssets):
One pair has to cover every bucket named in — the three required ones, plus if your manifest names it. Delete is required, not just read and write, because backup retention deletes the dumps it ages out and pgBackRest deletes the backups and WAL it expires — a key without it works until the first one ages out, then fails on every retention pass. A key scoped to only some of the buckets completes this command cleanly and fails much later, the first time a backup or a transformation runs.
configuration.objectStore.bucketsbuckets.postgresBackupsWhen the manifest names , the run also writes a second secret, , holding the pgBackRest config file that carries this same pair — pgBackRest reads credentials from a config file rather than from environment variables. It is derived from the pair above rather than prompted for, so it asks you for nothing extra, and it is re-derived on every run rather than being something you can supply or persist independently.
buckets.postgresBackupspgbackrest-s3-credentialsAWS_*Non-interactive runs need the pair in the file, under as and . A run without it leaves both at the placeholder and only logs a warning, so check the generated file before deploying it.
--imports3-api-storage-credentialsAWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY--use-defaultsTBDOn the in-cluster store, a file carries the generated keys rather than a pair of yours. An run does not read them — it warns that they, and , are not used — and prompts for your pair instead. A persist file written by an run no longer carries the keys, so switching back regenerates them.
--persistGARAGE_*s3garage-rpc-secrets3GARAGE_*On this provider the in-cluster store's is not generated either — there is no in-cluster store to hand it to. With (the default) nothing about this command changes.
garage-rpc-secretprovider: garageThe image pull secret
The generated file also carries the image pull secret named by , so deploying it creates that secret too and no hand-written is needed. It is built from:
configuration.kubernetes.imagePullSecret.namekubectl create secret docker-registry- the registry — which, when you pull straight from Unity's registry, is that registry;
configuration.kubernetes.docker.repository - the credentials you supply for (
acr-oras-credentials/ACR_USERNAME) in the same run. Those authenticate against the same registry, so they are the credentials the pull secret has to carry.ACR_PASSWORD
The credential is a point-in-time snapshot. Where the registry issues short-lived tokens — ECR's expire after 12 hours — something else has to keep the secret current; leave that owner in charge and opt out:
configuration: kubernetes: imagePullSecret: name: ecr-regcred generate: false # something else creates and refreshes it
The older spelling — the bare secret name — is still accepted and behaves exactly as before, except that the secret is now generated for you. shdctl logs a notice pointing at the block form when it reads it; move to the block form whenever it suits you.
imagePullSecret: ecr-regcredNothing is generated when the manifest names no pull secret. Nothing is generated either, with the reason logged, when the manifest opts out, gives it the name of a secret the release already defines, resolves no registry, or when the registry credentials were not supplied — a run with no import file leaves them at . shdctl never builds the pull secret from missing or placeholder credentials, so neither a pull secret already in the cluster nor another secret in this file is replaced by such a snapshot — but it cannot tell whether credentials you supply actually work.
--use-defaultsTBDDeploy secrets to Kubernetes
Apply the generated secrets to your Kubernetes cluster:
shdctl secret deploy
Common options:
- ,
--file: Path to the secrets YAML file (defaults to-f)secrets.yaml - : Preview the kubectl command without executing it
--dry-run - : Kubernetes context to use
--context - ,
--namespace: passed to-naskubectl. It cannot move the secrets: every one already names its namespace, and kubectl refuses a-nthat differs. To deploy them elsewhere, change-nand regenerateconfiguration.kubernetes.namespace
It runs , so the kubeconfig needs , and on secrets in the namespace. stamps that namespace into every secret from , which is why is never needed.
kubectl apply -f <file>getcreatepatchsecret generateconfiguration.kubernetes.namespace--namespaceExample:
shdctl secret deploy --file ./generated-secrets.yaml --dry-run
Recover the import file from a cluster
When the import file you deployed with is gone — it went out with an ephemeral bastion, or somebody else ran the deploy and never shared it — rebuild it from the secrets already in the cluster:
shdctl secret export --output secrets.import.yaml
The release's secrets schema drives the read: for every value the schema expects you to supply, the matching value is read out of the cluster and written back in import format. The values derived from those — connection strings and the like — are left out, because derives them again from the file. The result is the file would have written — except that a value still at the placeholder is listed below rather than copied — so you can pass it straight back to .
shdctl secret generate--persistTBD--importYou need the the deployment was generated from — it sets the namespace, and which of the conditional secrets (the object store's, for one) are read — and an extracted release for the schema ( if you no longer have one).
manifest.yamlshdctl release pullA namespace holding none of the secrets the schema describes — nothing deployed there yet, or the wrong namespace or context — is an error rather than an empty file: the command exits non-zero with and writes nothing. Any other failure means the cluster's secrets went unread, not that there were none, so do not generate from an empty file on the strength of it.
recovered no secret values from namespace "<namespace>"Common options:
- ,
--output: Output file path (defaults to-o)secrets.import.yaml - ,
--namespace: Namespace to read from (defaults to-n)configuration.kubernetes.namespace - : Kubernetes context to read from (defaults to the current one)
--context - : Overwrite the output file when it already exists — without it, an existing file is left alone, since it may hold values this cluster does not
--force - : Print the kubectl command and contact nothing
--dry-run - : Path to extracted release (defaults to
--extracted-release)./extracted-release - : Skip the shdctl version compatibility check against the release package
--skip-version-check
Values the cluster has nothing usable for are listed at the end — a secret the deployment predates, a key that was never set, or a field still carrying the placeholder. Only the ones you have to act on appear: a field the schema gives a default is left out, because generation resolves it from that default rather than inventing a new value.
TBDNOT RECOVERED (2) — supply these by hand before 'shdctl secret generate',or it will generate new values and deploying those will break whateverstill uses the old ones: pixyz-license: pixyz.lic (cluster holds the TBD placeholder, so no real value is set for it) acr-oras-credentials: ACR_PASSWORD (cluster holds the TBD placeholder, so no real value is set for it)
Fill those in before generating. To confirm the recovered file reproduces what is deployed, regenerate from it and ask the cluster what applying it would change — exits 0 when nothing would:
kubectl diffshdctl secret generate --import secrets.import.yaml --use-defaults --output /tmp/check.yamlkubectl diff -f /tmp/check.yaml -n <namespace>
Access this needs. It reads every secret in the namespace in one collection read — Helm's release records excepted, which the API server filters out — so the kubeconfig needs the verb on secrets there — not . An unnamed is a request, and a Role granting only fails with :
listgetkubectl get secretlistgetsecrets is forbidden: ... cannot list resource "secrets"rules: - apiGroups: [""] resources: ["secrets"] verbs: ["list"]
That is more than the // a deployment pipeline needs. Run it as an operator; do not widen a CI role for it. The file it writes holds your secrets in the clear (mode ): keep it in your secret store, not in Git.
getcreatepatch0600Import file format
An example import file for . Its comments say which values you supply and which ones shdctl generates:
shdctl secret generate# Example import file for shdctl secret generate# This file contains secret values organized by secret name# Each secret name maps to a dictionary of field keys and values# Values are plain text and base64-encoded for you — except the license files# (pixyz.lic, plasticd.lic), which are read as base64: encode the file's content## A value you leave out is generated only where the schema generates that field, and# then afresh on every run. A required value with no default — the registry# credentials, the licenses — is prompted for, or written as TBD under --use-defaults,# and a --persist file keeps that TBD: supply those values here, and check the# generated file for TBD before deploying it. Once a deployment exists, generate from# a file that carries every value: the one `shdctl secret generate --persist` writes,# or the one `shdctl secret export` rebuilds.## A value you supply for a generated field is checked like a generated one: passwords# are letters and digits only unless the schema allows more, novu-api's# STORE_ENCRYPTION_KEY is exactly 32 characters, and the UVCS passwords at least 18.# An RSA key you supply, such as asset-cloud-storage-abstraction's storage-key.pem,# must be a PKCS#1 key (-----BEGIN RSA PRIVATE KEY-----); convert one with# `openssl rsa -traditional -in key.pem -out key-pkcs1.pem`.## Format:# secret-name:# field-key: value# another-field: value# another-secret:# field-key: value# Credentials for the registry your cluster pulls images from# (configuration.kubernetes.docker.repository): your own registry's once you mirror# into it with `shdctl artifact sync`, or Unity's when you pull from it directly.# They make the image pull secret, and the automation manager pulls the automation# apps with them.acr-oras-credentials: ACR_USERNAME: "<your-registry-username>" ACR_PASSWORD: "<your-registry-password>"# UVCS SSL Certificate PEMcustom-ca-certificate-pem: ssl-certificate.pem: "<your-certificate-pem>"# Pixyz License (used by builtins workflow templates)pixyz-license: pixyz.lic: "<your-pixyz-license-content>"# Pixyz License for 3D Data Streamingpixyz-license-3dds: pixyz.lic: "<your-pixyz-3dds-license-content>"# Elasticsearch Secretselasticsearch-apps-credentials: password: "<your-password>"# Mini-usf Secretsmini-usf: MINIUSF_Keycloak__ClientSecret: "<your-client-secret>"# Novu API Secretsnovu-api: JWT_SECRET: "<your-jwt-secret>" STORE_ENCRYPTION_KEY: "<your-encryption-key>"# PSMDB Secrets - MongoDB/Percona Server for MongoDB secretspsmdb-secrets: MONGODB_BACKUP_PASSWORD: "<your-password>" MONGODB_CLUSTER_ADMIN_PASSWORD: "<your-password>" MONGODB_CLUSTER_MONITOR_PASSWORD: "<your-password>" MONGODB_DATABASE_ADMIN_PASSWORD: "<your-password>" MONGODB_USER_ADMIN_PASSWORD: "<your-password>"# PMM Admin Password (optional only if PMM is deployed)# pmm-admin-password:# GF_SECURITY_ADMIN_PASSWORD: "<your-password>"# PSMDB Asset Manager Passwordpsmdb-asset-manager-password: password: "<your-password>"# PostgreSQL Secretspg-db-pguser-assetsolutions: password: "<your-password>"# RabbitMQ Admin Usernamerabbitmq-asset-solutions-import-admin-user: password: "<your-password>"# S3 API Storage Credentials# Which key names apply depends on configuration.objectStore.provider in your manifest.# With the default in-cluster store (provider: garage), supply GARAGE_*; leave them out# and shdctl generates them for you.s3-api-storage-credentials: GARAGE_ACCESS_KEY: "<your-access-key>" GARAGE_SECRET_KEY: "<your-secret-key>"# With provider: s3, supply AWS_* instead — the credentials for your own object# store, able to read, write AND DELETE objects in every bucket named in# configuration.objectStore.buckets, including studioAssets, plus postgresBackups# when you set it (backup retention deletes the backups and dumps it ages out).# These cannot be generated, and GARAGE_* values are ignored on that provider.# AWS_ACCESS_KEY_ID: "<your-object-store-access-key-id>"# AWS_SECRET_ACCESS_KEY: "<your-object-store-secret-access-key>"# UVCS Licenseuvcs-license: plasticd.lic: "<your-license-content>"# UVCS configurationuvcs-configuration: UVCS_USER_PASSWORD: "<your-password>" UVCS_ADMIN_PASSWORD: "<your-password>"# Valkey Users ACLvalkey-users: users.acl.password: "<your-password>"