In a GitOps workflow, it can be challenging to handle sensitive data because you want to commit everything to git without revealing API keys, passwords or tokens. The Sealed Secrets project helps solve this problem.
In this chapter, you will learn how to create manifests using the Flux CLI
, to define the sealed secrets Helm release. Then, Flux
will trigger the Sealed Secrets Controller
installation process for your cluster so you can commit encrypted secrets to your Github repository and they will be decrypted
First, change directory where your Flux CD Git repository was cloned. Also, please check that the required directory structure for this tutorial is created, and that the FLUXCD_HELM_MANIFESTS_PATH
environment variable is set.
Now, use Flux to create the Sealed Secrets HelmRepository
resource
flux create source helm sealed-secrets \
--url="https://bitnami-labs.github.io/sealed-secrets" \
--interval="10m" \
--export > "${FLUXCD_HELM_MANIFESTS_PATH}/repositories/sealed-secrets.yaml"
Explanations for the above command:
--url
: Helm repository address.--interval
: Source sync interval (default1m0s
).--export
: Export inYAML
format to stdout.
The output looks similar to this:
---
apiVersion: source.toolkit.fluxcd.io/v1beta1
kind: HelmRepository
metadata:
name: sealed-secrets
namespace: flux-system
spec:
interval: 10m0s
url: https://bitnami-labs.github.io/sealed-secrets
Please make sure to inspect the values file first, and replace the <>
placeholders where needed:
SEALED_SECRETS_CHART_VERSION="2.1.6"
curl "https://raw.githubusercontent.com/digitalocean/Kubernetes-Starter-Kit-Developers/main/08-kubernetes-sealed-secrets/assets/manifests/sealed-secrets-values-v${SEALED_SECRETS_CHART_VERSION}.yaml" > "sealed-secrets-values-v${SEALED_SECRETS_CHART_VERSION}.yaml"
Kubeseal
CLI expects by default to find the controller in the kube-system
namespace and to be named sealed-secrets-controller
, hence we override the release name via the --release-name
and --target-namespace
flags. This is not mandatory, but kube-system
is usually accessible only to power users (administrators):
SEALED_SECRETS_CHART_VERSION="2.1.6"
flux create helmrelease "sealed-secrets-controller" \
--release-name="sealed-secrets-controller" \
--source="HelmRepository/sealed-secrets" \
--chart="sealed-secrets" \
--chart-version "$SEALED_SECRETS_CHART_VERSION" \
--values="sealed-secrets-values-v${SEALED_SECRETS_CHART_VERSION}.yaml" \
--target-namespace="flux-system" \
--crds=CreateReplace \
--export > "${FLUXCD_HELM_MANIFESTS_PATH}/releases/sealed-secrets-v${SEALED_SECRETS_CHART_VERSION}.yaml"
Explanations for the above command:
--release-name
: What name to use for the Helm release (defaults to<target-namespace>-<HelmRelease-name>
otherwise).--source
: Source that contains the chart in the format<kind>/<name>.<namespace>
, where kind must be one of: (HelmRepository
,GitRepository
,Bucket
).--chart
: Helm chart name.--chart-version
: Helm chart version.--values
: Local path to values file.--target-namespace
: Namespace to install this release.--crds
: Upgrade CRDs policy, available options are: (Skip
,Create
,CreateReplace
).--export
: Export inYAML
format to stdout.
The output looks similar to this:
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
name: sealed-secrets-controller
namespace: flux-system
spec:
chart:
spec:
chart: sealed-secrets
sourceRef:
kind: HelmRepository
name: sealed-secrets
version: 2.1.6
interval: 1m0s
releaseName: sealed-secrets-controller
targetNamespace: flux-system
install:
crds: Create
upgrade:
crds: CreateReplace
values:
ingress:
enabled: false
SEALED_SECRETS_CHART_VERSION="2.1.6"
git add "${FLUXCD_HELM_MANIFESTS_PATH}/repositories/sealed-secrets.yaml"
git add "${FLUXCD_HELM_MANIFESTS_PATH}/releases/sealed-secrets-v${SEALED_SECRETS_CHART_VERSION}.yaml"
git commit -am "Adding Sealed Secrets manifests for Flux CD"
git push origin
After completing the above steps, Flux CD
will start your cluster reconciliation (in about one minute
or so, if using the default
interval). If you don't want to wait, you can always force
reconciliation via:
flux reconcile source git flux-system
After a few moments, please inspect the Flux CD sealed secrets Helm release:
flux get helmrelease sealed-secrets-controller
The output looks similar to:
NAME READY MESSAGE REVISION SUSPENDED
sealed-secrets-controller True Release reconciliation succeeded 2.1.6 False
Look for the READY
column value - it should say True
. Reconciliation status is displayed in the MESSAGE
column, along with the REVISION
number, which represents the Helm
chart version
. Please bear in mind that some releases take longer to complete (like Prometheus
stack, for example), so please be patient.
Hints:
- The
MESSAGE
column will displayReconciliation in progress
, as long as theHelmController
is performing the installation for the specifiedHelm
chart. If something goes wrong, you'll get another message stating the reason, so please make sure to check Helm release state. - You can use the
--watch
flag for example:flux get helmrelease <name> --wait
, to wait until the command finishes. Please bear in mind that in this mode,Flux
will block your terminal prompt until the default timeout of5 minutes
occurs (can be overridden via the--timeout
flag). - In case something goes wrong, you can search the
Flux
logs, and filterHelmRelease
messages only:
flux logs --kind=HelmRelease
To be able to encrypt
secrets, you need the public key that was generated by the Sealed Secrets Controller
when it was deployed by Flux CD
in your DOKS
cluster.
First, change directory where you cloned your Flux CD
Git repository, and do the following
kubeseal --controller-namespace=flux-system --fetch-cert > pub-sealed-secrets.pem
Note:
If for some reason the kubeseal
certificate fetch command hangs (or you get an empty/invalid certificate file), you can use the following steps to work around this issue:
- First, open a new terminal window, and
expose
theSealed Secrets Controller
service on yourlocalhost
(you can useCTRL - C
to terminate, after fetching the public key):
kubectl port-forward service/sealed-secrets-controller 8080:8080 -n flux-system
- Then, you can go back to your working terminal and fetch the public key
curl --retry 5 --retry-connrefused localhost:8080/v1/cert.pem > pub-sealed-secrets.pem
Finally, commit
the public key file to remote Git
repository for later use (it's safe to do this, because the public key
is useless without the private key
which is stored in your cluster only). Please run bellow commands, and make sure to replace the <>
placeholders accordingly:
git add pub-sealed-secrets.pem
git commit -m "Adding Sealed Secrets public key for cluster"
git push origin
Important note:
In this tutorial the flux-system
namespace is used to hold Kubernetes Secrets
, so please make sure that it is restricted
to regular users/ applications via RBAC
.
In this step, you will learn how to encrypt a generic Kubernetes
secret, using kubeseal
CLI. Then, you will deploy it to your cluster and see how the sealed secrets controller decrypts
it for your applications to use.
Suppose that you need to seal a generic secret for your application, saved in the following file: your-app-secret.yaml
. Notice the your-data
field which is base64
encoded (it's vulnerable
to attacks, because it can be very easily decoded
using free tools):
apiVersion: v1
kind: Secret
metadata:
name: your-app
data:
your-data: ZXh0cmFFbnZWYXJzOgogICAgRElHSVRBTE9DRUFOX1RPS0VOOg== # base64 encoded application data
Next, create a sealed
file from the Kubernetes
secret, using the pub-sealed-secrets.pem
key:
kubeseal --format=yaml \
--namespace=flux-system \
--cert=pub-sealed-secrets.pem \
--secret-file your-app-secret.yaml \
--sealed-secret-file your-app-sealed.yaml
The new file defines a SealedSecret CRD:
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
creationTimestamp: null
name: your-app
namespace: flux-system
spec:
encryptedData:
your-data: AgBp1WS9Am1v5Mzb0VqqLXU498OJEuBynnMZH+Z1Zq8g4esy6L4dEZ5stjOhliiPsCKqCVVDGW9wKsley3NOaUBsxXy/zbTuvzPnWyFQLtDAlbpL8Oo4RIe5rnzbuUYUAjtrvfeZnzMRTd/CEU4BUl/nZ6WbYwesigRMZC/2D/7ypJrPw1O18VBp9D/Nc21qaFt6E6Q8VQcPNZ5Vywsjq+iArFe2Tqz30q3c/jIsdys0YxPxfttoI3Q0zAnNnlr3Ubsuo8eDF5U3zZ43wDyAi5JvqEhHGHdK8c3xtMBL5rnyOfr/dbihj1nuHIbF6aMSyGhf0QHwl+y9XBgy5ZOoS0R/1jKbzqaIge+aBxPdR5KvH/anIFHjVDQMn16hv/Ccly9S/UObenyyXGt+vJkixDPTP8rR8ZGJtmpvjc2CFjXyWtVKW1C16AD4uRbzHduxt10Af6Co2DNS/a3DMCLkRvxJBCZATRKtvuPCXP6xpYUWQq0T9p+SURzMPIRzzJWinuChIm8EpkPF3oh1wesps7wlZbdYagtnTQ4THsxpT8A7kpyB14iB/S4b9MDpEThGmxKeeuYu30FFYlt4cjW2kyX4y+eHPrHiqsCl57RbEYKNElaT87g0YnrvfQHkLD54X02GxdeQ9+rnL4yXerp42m7ilB/yFZqapENdlFKttNe6uUh/A7Co1Bu/Q2vdKJz6Fafb1d5/KttqdZyG+Jrd35LqIxLStWZNTZj6pa7B1jEA4C8GmpBr
template:
data: null
metadata:
creationTimestamp: null
name: your-app
namespace: flux-system
Note:
If you don't specify a namespace
, the default
one is assumed (use kubeseal --namespace
flag, to change targeted namespace). Default scope
used by kubeseal
is strict
- please refer to scopes in Security Best Practices.
Next, you can delete the Kubernetes
secret file, because it's not needed anymore:
rm -f your-app-secret.yaml
Finally, deploy
the sealed secret
to your cluster:
kubectl apply -f your-app-sealed.yaml
Check that the Sealed Secrets Controller
decrypted your Kubernetes
secret in the flux-system
namespace:
kubectl get secrets -n flux-system
The output looks similar to:
NAME TYPE DATA AGE
your-app Opaque 1 31s
Inspect the secret:
kubectl get secret your-app -o yaml -n flux-system
The output looks similar to (your-data
key value
should be decrypted
to the original base64
encoded value
):
apiVersion: v1
data:
your-data: ZXh0cmFFbnZWYXJzOgogICAgRElHSVRBTE9DRUFOX1RPS0VOOg==
kind: Secret
metadata:
creationTimestamp: "2021-10-05T08:34:07Z"
name: your-app
namespace: flux-system
ownerReferences:
- apiVersion: bitnami.com/v1alpha1
controller: true
kind: SealedSecret
name: your-app
uid: f6475e74-78eb-4c6a-9f19-9d9ceee231d0
resourceVersion: "235947"
uid: 7b7d2fee-c48a-4b4c-8f16-2e58d25da804
type: Opaque
If you want to perform a manual backup of the private and public keys, you can do so via:
kubectl get secret -n flux-system -l sealedsecrets.bitnami.com/sealed-secrets-key -o yaml > master.key
Then, store the master.key
file somewhere safe. To restore from a backup after some disaster, just put that secret back before starting the controller - or if the controller was already started, replace the newly-created secrets and restart the controller:
kubectl apply -f master.key
kubectl delete pod -n flux-system -l name=sealed-secrets-controller
In terms of security, sealed secrets allows you to restrict
other users to decrypt your sealed secrets inside the cluster. There are three scopes
that you can use (kubeseal
CLI --scope
flag):
strict
(default): the secret must be sealed withexactly
the samename
andnamespace
. Theseattributes
becomepart
of theencrypted data
and thuschanging name
and/ornamespace
would lead to "decryption error".namespace-wide
: you can freelyrename
the sealed secret within a givennamespace
.cluster-wide
: thesecret
can beunsealed
in anynamespace
and can be given anyname
.