Problem: "I can manage all my K8s config in git, except Secrets."
Solution: Encrypt your Secret into a SealedSecret, which is safeto store - even to a public repository. The SealedSecret can bedecrypted only by the controller running in the target cluster andnobody else (not even the original author) is able to obtain theoriginal Secret from the SealedSecret.
Table of Contents
kube-system
namespace?Sealed Secrets is composed of two parts:
kubeseal
The kubeseal
utility uses asymmetric crypto to encrypt secrets that only the controller can decrypt.
These encrypted secrets are encoded in a SealedSecret
resource, which you can see as a recipe for creatinga secret. Here is how it looks:
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: mysecret
namespace: mynamespace
spec:
encryptedData:
foo: AgBy3i4OJSWK+PiTySYZZA9rO43cGDEq.....
Once unsealed this will produce a secret equivalent to this:
apiVersion: v1
kind: Secret
metadata:
name: mysecret
namespace: mynamespace
data:
foo: bar # <- base64 encoded "bar"
This normal kubernetes secret will appear in the clusterafter a few seconds and you can use it as you would use any secret that you would have created directly (e.g. reference it from a Pod
).
Jump to the Installation section to get up and running.
The Usage section explores in more detail how you craft SealedSecret
resources.
The previous example only focused on the encrypted secret items themselves, but the relationship between a SealedSecret
custom resource and the Secret
it unseals into is similar in many ways (but not in all of them) to the familiar Deployment
vs Pod
.
In particular, the annotations and labels of a SealedSecret
resource are not the same as the annotations of the Secret
that gets generated out of it.
To capture this distinction, the SealedSecret
object has a template
section which encodes all the fields you want the controller to put in the unsealed Secret
.
This includes metadata such as labels or annotations, but also things like the type
of the secret.
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: mysecret
namespace: mynamespace
annotation:
"kubectl.kubernetes.io/last-applied-configuration": ....
spec:
encryptedData:
.dockerconfigjson: AgBy3i4OJSWK+PiTySYZZA9rO43cGDEq.....
template:
type: kubernetes.io/dockerconfigjson
# this is an example of labels and annotations that will be added to the output secret
metadata:
labels:
"jenkins.io/credentials-type": usernamePassword
annotations:
"jenkins.io/credentials-description": credentials from Kubernetes
The controller would unseal that into something like:
apiVersion: v1
kind: Secret
metadata:
name: mysecret
namespace: mynamespace
labels:
"jenkins.io/credentials-type": usernamePassword
annotations:
"jenkins.io/credentials-description": credentials from Kubernetes
ownerReferences:
- apiVersion: bitnami.com/v1alpha1
controller: true
kind: SealedSecret
name: mysecret
uid: 5caff6a0-c9ac-11e9-881e-42010aac003e
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: ewogICJjcmVk...
As you can see, the generated Secret
resource is a "dependent object" of the SealedSecret
and as suchit will be updated and deleted whenever the SealedSecret
object gets updated or deleted.
The key certificate (public key portion) is used for sealing secrets,and needs to be available wherever kubeseal
is going to beused. The certificate is not secret information, although you need toensure you are using the correct one.
kubeseal
will fetch the certificate from the controller at runtime(requires secure access to the Kubernetes API server), which isconvenient for interactive use, but it's known to be brittle when usershave clusters with special configurations such as private GKE clusters that havefirewalls between master and nodes.
An alternative workflowis to store the certificate somewhere (e.g. local disk) withkubeseal --fetch-cert >mycert.pem
,and use it offline with kubeseal --cert mycert.pem
.The certificate is also printed to the controller log on startup.
Since v0.9.x certificates get automatically renewed every 30 days. It's good practice that you and your teamupdate your offline certificate periodically. To help you with that, since v0.9.2 kubeseal
accepts URLs too. You can setup your internal automation to publish certificates somewhere you trust.
kubeseal --cert https://your.intranet.company.com/sealed-secrets/your-cluster.cert
It also recognizes the SEALED_SECRETS_CERT
env var. (pro-tip: see also direnv).
NOTE: we are working on providing key management mechanisms that offload the encryption to HSM based modules or managed cloud crypto solutions such as KMS.
SealedSecrets are from the POV of an end user a "write only" device.
The idea is that the SealedSecret can be decrypted only by the controller running in the target cluster andnobody else (not even the original author) is able to obtain the original Secret from the SealedSecret.
The user may or may not have direct access to the target cluster.More specifically, the user might or might not have access to the Secret unsealed by the controller.
There are many ways to configure RBAC on k8s, but it's quite common to forbid low-privilege usersfrom reading Secrets. It's also common to give users one or more namespaces where they have higher privileges,which would allow them to create and read secrets (and/or create deployments that can reference those secrets).
Encrypted SealedSecret are designed to be safe to be looked at without gaining any knowledge about the secrets it conceals. This implies that we cannot allow users to read a SealedSecret meant for a namespace they wouldn't have access toand just push a copy of it in a namespace where they can read secrets from.
Sealed-secrets thus behaves as if each namespace had its own independent encryption key and thus once youseal a secret for a namespace, it cannot be moved in another namespace and decrypted there.
We don't technically use an independent private key for each namespace, but instead we include the namespace nameduring the encryption process, effectively achieving the same result.
Furthermore, namespaces are not the only level at which RBAC configurations can decide who can see which secret. In fact, it's possible that users can access a secret called foo
in a given namespace but not any other secret in the same namespace. We cannot thus by default let users freely rename SealedSecret resources otherwise a malicious user would be able to decrypt any SealedSecret for that namespace by just renaming it to overwrite the one secret she does have access to. We use the same mechanism used to include the namespace in the encryption key to also include the secret name.
That said, there are many scenarios where you might not care about this level of protection. For example, the only people who have access to your clusters are either admins or they cannot read any secret resource at all. You might have a use case for moving a sealed secret to other namespaces (e.g. you might not know the namespace name upfront), or you might not know the name of the secret (e.g. it could contain a unique suffix based on the hash of the contents etc).
These are the possible scopes:
strict
(default): the secret must be sealed with exactly the same name and namespace. These attributes become part of the encrypted data and thus changing name and/or namespace would lead to "decryption error".namespace-wide
: you can freely rename the sealed secret within a given namespace.cluster-wide
: the secret can be unsealed in any namespace and can be given any name.In contrast to the restrictions of name and namespace, secret items (i.e. JSON object keys like spec.encryptedData.my-key
) can be renamed at will without losing the ability to decrypt the sealed secret.
The scope is selected with the --scope
flag:
$ kubeseal --scope cluster-wide <secret.yaml >sealed-secret.json
It's also possible to request a scope via annotations in the input secret you pass to kubeseal
:
sealedsecrets.bitnami.com/namespace-wide: "true"
-> for namespace-wide
sealedsecrets.bitnami.com/cluster-wide: "true"
-> for cluster-wide
The lack of any of such annotations means strict
mode. If both are set, cluster-wide
takes precedence.
NOTE: next release will consolidate this into a single
sealedsecrets.bitnami.com/scope
annotation.
See https://github.com/bitnami-labs/sealed-secrets/releases for the latestrelease and detailed installation instructions.
Cloud platform specific notes and instructions:
Once you deploy the manifest it will create the SealedSecret
resourceand install the controller into kube-system
namespace, create a serviceaccount and necessary RBAC roles.
After a few moments, the controller will start, generate a key pair,and be ready for operation. If it does not, check the controllerlogs.
The official controller manifest installation mechanism is just a YAML file.
In some cases you might need to apply your own customizations, like set a custom namespace or set some env variables.
kubectl
has native support for that, see kustomize.
The Sealed Secrets helm chart is now official supported and hosted in this GitHub repo.
$ helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets
NOTE: The versioning scheme of the helm chart differs from the versioning scheme of the sealed secrets project itself.
Originally the helm chart was maintained by the community and the first version adopted a major version of 1 while thesealed secrets project itself is still at major 0.This is ok because the version of the helm chart itself is not meant to be necessarily the version of the app itself.However this is confusing, so our current versioning rule is:
There can be thus multiple revisions of the helm chart, with fixes that apply only to the helm chart withoutaffecting the static YAML manifests or the controller image itself.
NOTE: the helm chart readme still contains a deprecation notice but it's no longer reflects realityand will be removed upon next release.
Install Sealed Secrets as Kubernetes Operator via the Operator Lifecyle Manager of your cluster. The Sealed Secrets Operator (Helm)
is published at OperatorHub.io for Kubernetes, as community operator in OpenShift's integrated OperatorHub or at the GitHub repository of the project.
NOTE: the sealed secrets operator is an independently maintained project, so please contact the maintainers directly for support, help or documentation.
The kubeseal
client is also available on homebrew:
$ brew install kubeseal
The kubeseal
client is also available on MacPorts:
$ port install kubeseal
If you just want the latest client tool, it can be installed into$GOPATH/bin
with:
% (cd /; GO111MODULE=on go get github.com/bitnami-labs/sealed-secrets/cmd/kubeseal@main)
You can specify a release tag or a commit SHA instead of main
.
Don't forget to check the release notes for guidance aboutpossible breaking changes when you upgrade the client tooland/or the controller.
# Create a json/yaml-encoded Secret somehow:
# (note use of `--dry-run` - this is just a local file!)
$ echo -n bar | kubectl create secret generic mysecret --dry-run=client --from-file=foo=/dev/stdin -o json >mysecret.json
# This is the important bit:
# (note default format is json!)
$ kubeseal <mysecret.json >mysealedsecret.json
# mysealedsecret.json is safe to upload to github, post to twitter,
# etc. Eventually:
$ kubectl create -f mysealedsecret.json
# Profit!
$ kubectl get secret mysecret
Note the SealedSecret
and Secret
must have the same namespace andname. This is a feature to prevent other users on the same clusterfrom re-using your sealed secrets. See the Scopes section for more info.
kubeseal
reads the namespace from the input secret, accepts an explicit --namespace
arg, and usesthe kubectl
default namespace (in that order). Any labels,annotations, etc on the original Secret
are preserved, but notautomatically reflected in the SealedSecret
.
By design, this scheme does not authenticate the user. In otherwords, anyone can create a SealedSecret
containing any Secret
they like (provided the namespace/name matches). It is up to yourexisting config management workflow, cluster RBAC rules, etc to ensurethat only the intended SealedSecret
is uploaded to the cluster. Theonly change from existing Kubernetes is that the contents of theSecret
are now hidden while outside the cluster.
If you want SealedSecret
controller to take management of an existing Secret
(i.e. overwrite it when unsealing a SealedSecret
with the same name and namespace), then you have to annotate that Secret
with the annotation sealedsecrets.bitnami.com/managed: "true"
ahead applying the Usage steps.
If you want to add or update existing sealed secrets without having the cleartext for the other items,you can just copy&paste the new encrypted data items and merge it into an existing sealed secret.
You must take care of sealing the updated items with a compatible name and namespace (see note about scopes above).
You can use the --merge-into
command to update an existing sealed secrets if you don't want to copy&paste:
$ echo -n bar | kubectl create secret generic mysecret --dry-run=client --from-file=foo=/dev/stdin -o json \
| kubeseal > mysealedsecret.json
$ echo -n baz | kubectl create secret generic mysecret --dry-run=client --from-file=bar=/dev/stdin -o json \
| kubeseal --merge-into mysealedsecret.json
Creating temporary Secret with the kubectl
command, only to throw it away once piped to kubeseal
canbe a quite unfriendly user experience. We're working on an overhaul of the the CLI experience. In the meantime,we offer an alternative mode where kubeseal only cares about encrypting a value to stdout and it's your responsibility to put it inside a SealedSecret resource (not unlike any of the other k8s resources).
It can also be useful as a building block for editor/IDE integrations.
The downside is that you have to be careful to be consistent with the sealing scope, the namespace and the name.See Scopes:
$ echo -n foo | kubeseal --raw --from-file=/dev/stdin --namespace bar --name mysecret
AgBChHUWLMx...
$ echo -n foo | kubeseal --raw --from-file=/dev/stdin --namespace bar --scope namespace-wide
AgAbbFNkM54...
$ echo -n foo | kubeseal --raw --from-file=/dev/stdin --scope cluster-wide
AgAjLKpIYV+...
You should always rotate your secrets. But since your secrets are encrypted with another secret,you need to understand how these two layers relate in order to take the right decisions.
TL;DR:
If a sealing private key is compromised, you need to follow the instructions below in "Early key renewal"section before rotating any of your actual secret values.
SealedSecret key renewal and re-encryption features are not a substitute for periodical rotation of your actual secret values.
Sealing keys are automatically renewed every 30 days. Which means a new sealing key is created and appended to the set of active sealing keys the controller can use to unseal Sealed Secret resources.
The most recently created sealing key is the one used to seal new secrets when you use kubeseal
and it's the one whose certificate is downloaded when you use kubeseal --fetch-cert
.
The renewal time of 30d is a reasonable default, but it can be tweaked as neededwith the --key-renew-period=<value>
flag for the command in the pod template of the sealed secret controller. The value
field can be given as golangduration flag (eg: 720h30m
).
A value of 0
will disable automatic key renewal. Of course, it's possible you have a valid use case for disabling automatic sealing key renewal; but experience has shown that new users often tend to jump to conclusions that they want control over key renewal, before fully understanding how sealed secrets work. Read more about this in the common misconceptions section below.
Unfortunately you cannot use e.g. "d" as a unit for days because that's not supported by the Go stdlib. Instead of hitting your face with a palm, take this as an opportunity to meditate on the falsehoods programmers believe about time.
A common misunderstanding is that key renewal is often thought of as a form of key rotation, where the old key is not only obsolete but actually bad and that you thus want to get rid of it.It doesn't help that this feature has been historically called "key rotation", which can add to the confusion.
Sealed secrets are not automatically rotated and old keys are not deletedwhen new keys are generated. Old sealed secrets resources can be still decrypted (that's because old sealing keys are not deleted).
The sealing key renewal and SealedSecret rotation are not a substitute for rotating your actual secrets.
A core value proposition of this tool is:
Encrypt your Secret into a SealedSecret, which is safe to store - even to a public repository.
If you store anything in a version control storage, and in a public one in particular, you must assumeyou cannot ever delete that information.
If a sealing key somehow leaks out of the cluster you must consider all your SealedSecret resourcesencrypted with that key as compromised. No amount of sealing key rotation in the cluster or even re-encryption of existing SealedSecrets files can change that.
The best practice is to periodically rotate all your actual secrets (e.g. change the password) and craft newSealedSecret resource with those new secrets.
But if the sealed secrets controller were not renewing the sealing key that rotation would be moot,since the attacker could just decrypt the new secrets as well. Thus you need to do both: periodically renew the sealing key and rotate your actual secrets!
If you know or suspect a sealing key has been compromised you should renew the key ASAP before youstart sealing your new rotated secrets, otherwise you'll be giving attackers access to your new secrets as well.
A key can be generated early by passing the current timestamp to the controller into a flag called --key-cutoff-time
or an env var called SEALED_SECRETS_KEY_CUTOFF_TIME
. Expected format is RFC1123, you can generate it with the date -R
unix command.
Sealed secrets sealing keys are not access control keys (e.g. like a password); they are more like the GPG key you might use to read encrypted mail sent to you. Let's continue with the email analogy for a bit:
Imagine you have reasons to believe your private GPG key might have been compromised. You'd have more to lose than to gain if the first thing you do is to just delete your private key. All the previous emails sent with that key are no longer accessible to you (unless you have a decrypted copy of those emails), nor are new emails sent by your friends whom you have not yet managed to tell to use the new key.
Sure, the content of those encrypted emails is not secure, as an attacker might now be able to decrypt them, but what's done is done. Your sudden loss of ability to read those emails surely doesn't undo the damage; if anything, it's worse because you no longer know for sure what secret the attacker got to know. What you really want to do is to make sure that your friend stops using your old key and that from now on all further communication is encrypted with a new key pair (i.e. your friend must know about that new key).
The same logic applies to SealedSecrets. The ultimate goal is securing your actual "user" secrets. The "sealing" secrets are just a mechanism, an "envelope". If a secret is leaked there is no going back; what's done is done.
You first need to ensure that new secrets don't get encrypted with that old compromised key (in the email analogy above that's: create a new keypair and give all your friends your new public key).
The second logical step is to neutralize the damage, which depends on the nature of the secret. A simple example is a database password: if you accidentally leak your database password, the thing you're supposed to do is simply to change your database password (on the database; and revoke the old one!) and update the SealedSecret resource with the new password (i.e. running kubeseal
again).
Both steps are described in the previous sections, albeit in a less verbose way. There is no shame in reading them again, now that you have a more in-depth grasp of the underlying rationale.
The sealed secrets controller and the associated workflow is designed to keep old sealing keys around and periodically add new ones. You should not delete old keys unless you know what you're doing.
That said, if you want you can manually manage (create, move, delete) sealing keys. They are just normal k8s secrets living in the same namespace where the sealed secret controller lives (usually kube-system
, but it's configurable).
There are advanced use cases that you can address by creative management of the sealing keys.For example, you can share the same sealing key among a few clusters so that you can apply exactly the same sealed secret in multiple clusters.Since sealing keys are just normal k8s secrets you can even use sealed secrets itself and use a GitOps workflow to manage your sealing keys (useful when you want to share the same key among different clusters)!
Labelling a sealing key secret with anything other than active
effectively deletesthe key from the sealed secrets controller, but it is still available in k8s formanual encryption/decryption if need be.
NOTE Sealed secrets currently does not automatically pick up manually created, deleted or relabeled sealing keys, an admin must restart the controller before the effect will apply.
Before you can get rid of some old sealing keys you need to re-encrypt your SealedSecrets with the latest private key.
kubeseal --re-encrypt <my_sealed_secret.json >tmp.json \
&& mv tmp.json my_sealed_secret.json
The invocation above will produce a new sealed secret file freshly encrypted withthe latest key, without making the secrets leave the cluster to the client. You can then save that filein your version control system (kubeseal --re-encrypt
doesn't update the in-cluster object).
Currently old keys are not garbage collected automatically.
It's a good idea to periodically re-encrypt your SealedSecrets. But as mentioned above, don't lull yourself in a false sense of security: you must assume the old version of the SealedSecret (the one encrypted with a key you think of as dead) is still potentially around and accessible to attackers. I.e. re-encryption is not a substitute for periodically rotating your actual secrets.
This controller adds a new SealedSecret
custom resource. Theinteresting part of a SealedSecret
is a base64-encodedasymmetrically encrypted Secret
.
The controller maintains a set of private/public key pairs as kubernetessecrets. Keys are labelled with sealedsecrets.bitnami.com/sealed-secrets-key
and identified in the label as either active
or compromised
. On startup,The sealed secrets controller will...
More details about crypto can be found here.
To be able to develop on this project, you need to have the following tools installed:
To build the kubeseal
and controller binaries, run:
$ make
To run the unit tests:
$ make test
To run the integration tests:
GOOS=linux GOARCH=amd64
to %-static
, and then run make controller.yaml IMAGE_PULL_POLICY=Never
kubectl apply -f controller.yaml
make clean
make
make integrationtest
To update the jsonnet dependencies:
$ jb install --jsonnetpkg-home=vendor_jsonnet
No, the private keys are only stored in the Secret managed by the controller (unless you have some other backup of your k8s objects). There are no backdoors - without that private key used to encrypt a given SealedSecrets, you can't decrypt it. If you can't get to the Secrets with the encryption keys, and you also can't get to the decrypted versions of your Secrets live in the cluster, then you will need to regenerate new passwords for everything, seal them again with a new sealing key, etc.
If you do want to make a backup of the encryption private keys, it's easy to do from an account with suitable access and:
$ kubectl get secret -n kube-system -l sealedsecrets.bitnami.com/sealed-secrets-key -o yaml >master.key
$ kubectl get secret -n kube-system sealed-secrets-key -o yaml >>master.key
NOTE: you need the second statement only if you ever installed sealed-secrets older than version 0.9.x on your cluster.
NOTE: This file will contains the controller's public + private keys and should be kept omg-safe!
To restore from a backup after some disaster, just put that secrets 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 kube-system -l name=sealed-secrets-controller
While treating sealed-secrets as long term storage system for secrets is not the recommended use case, some peopledo have a legitimate requirement for being able to recover secrets when the k8s cluster is down and restoring a backup into a new sealed-secrets controller deployment is not practical.
If you have backed up one or more of your private keys (see previous question), you can use the kubeseal --recovery-unseal --recovery-private-key file1.key,file2.key,...
command to decrypt a sealed secrets file.
You can check the flags available using kubeseal --help
.
A kubernetes secret resource contains multiple items, basically a flat map of key/value pairs.SealedSecrets operate at that level, and does not care what you put in the values. In other wordsit cannot make sense of any structured configuration file you might have put in a secret and thuscannot help you update individual fields in it.
Since this is a common problem, especially when dealing with legacy applications, we do offer an example of a possible workaround.
Yes, you can provide the controller with your own certificates so it will consume them.Please check here for a workaround.
kube-system
namespace?If you installed the controller in a different namespace than the default kube-system
, you need to provide this namespaceto the kubeseal
commandline tool. There are two options: You can specify the namespace via the command line option--controller-namespace <namespace>
or via the environment variable SEALED_SECRETS_CONTROLLER_NAMESPACE
.
Example:
# Provide the namespace via the command line option
$ kubeseal --controller-namespace sealed-secrets <mysecret.json >mysealedsecret.json
# Provide the namespace via the environment variable
$ export SEALED_SECRETS_CONTROLLER_NAMESPACE=sealed-secrets
$ kubeseal <mysecret.json >mysealedsecret.json
Click here to sign up to the Kubernetes Slack org.
主要内容:Java16 sealed密封类的示例Java 15 引入了一个密封类作为预览功能,它提供了对继承的细粒度控制。Java 16 提供了一些小的增强功能,并将此功能保留为预览版。以下是密封类需要考虑的要点 : 密封类是使用 sealed 关键字声明的。 密封类允许使用 permit 关键字声明哪个类可以是子类型。 扩展密封类的类必须声明为sealed 、non-sealed或final的。 密封类有助于在继承中创建有限且可确定的类层次结
主要内容:Java15 sealed 密封类的示例Java 15 引入了一个密封类作为预览功能,它提供了对继承的细粒度控制。以下是密封类需要考虑的要点 : 密封类是使用 sealed 关键字声明的。 密封类允许使用 permit 关键字声明哪个类可以是子类型。 继承密封类的类必须声明为sealed 、non-sealed或final的。 密封类有助于在继承中创建有限且可确定的类层次结构。 Java15 sealed 密封类的示例 ApiTeste
主要内容: Record的sealed密封类的示例由于默认情况下Record是final的,并且可以继承接口。我们可以定义密封接口并让Record实现它们以更好地管理代码。 Record的sealed密封类的示例 ApiTester.java 编译并运行程序 输出结果为
密封类用来限制类的继承关系,这意味着密封类的子类数量是固定的。看起来就像是枚举那样,当你想在一个密封类的子类中寻找一个指定的类的时候,你可以事先知道所有的子类。不同之处在于枚举的实例是唯一的,而密封类可以有很多实例,它们可以有不同的状态。 我们可以实现,比如类似Scala中的Option类:这种类型可以防止null的使用,当对象包含一个值时返回Some类,当对象为空时则返回None: sealed
本文向大家介绍C#中用sealed修饰的类有什么特点?相关面试题,主要包含被问及C#中用sealed修饰的类有什么特点?时的应答技巧和注意事项,需要的朋友参考一下 答: 密封,不能继承。
本文向大家介绍ASP.NET Core优雅的在开发环境保存机密(User Secrets),包括了ASP.NET Core优雅的在开发环境保存机密(User Secrets)的使用技巧和注意事项,需要的朋友参考一下 前言 在应用程序开发的过程中,有的时候需要在代码中保存一些机密的信息,比如加密密钥,字符串,或者是用户名密码等。通常的做法是保存到一个配置文件中,在以前我们会把他保存到web.conf