mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 11:21:47 +00:00
commit
d010b2a34e
333
docs/secrets.md
Normal file
333
docs/secrets.md
Normal file
@ -0,0 +1,333 @@
|
||||
# Secrets
|
||||
|
||||
Objects of type `secret` are intended to hold sensitive information, such as
|
||||
passwords, OAuth tokens, and ssh keys. Putting this information in a `secret`
|
||||
is safer and more flexible than putting it verbatim in a `pod` definition or in
|
||||
a docker image.
|
||||
|
||||
### Creating and Using Secrets
|
||||
To make use of secrets requires at least two steps:
|
||||
1. create a `secret` resource with secret data
|
||||
1. create a pod that has a volume of type `secret` and a container
|
||||
which mounts that volume.
|
||||
|
||||
This is an example of a simple secret, in json format:
|
||||
```json
|
||||
{
|
||||
"apiVersion": "v1beta3",
|
||||
"kind": "Secret",
|
||||
"name": "mysecret",
|
||||
"namespace": "myns",
|
||||
"data": {
|
||||
"username": "dmFsdWUtMQ0K",
|
||||
"password": "dmFsdWUtMg0KDQo="
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The data field is a map.
|
||||
Its keys must match [DNS_SUBDOMAIN](docs/design/identifiers.md).
|
||||
The values are arbitrary data, encoded using base64.
|
||||
|
||||
This is an example of a pod that uses a secret, in json format:
|
||||
```json
|
||||
{
|
||||
"apiVersion":"v1beta3",
|
||||
"name": "mypod",
|
||||
"kind": "Pod",
|
||||
"spec": {
|
||||
"manifest": {
|
||||
"containers": [{
|
||||
"name": "c",
|
||||
"image": "example/image",
|
||||
"volumeMounts": [{
|
||||
"name": "foo",
|
||||
"mountPath": "/etc/foo",
|
||||
"readOnly": true
|
||||
}]
|
||||
}],
|
||||
"volumes": [{
|
||||
"name": "foo",
|
||||
"source": {
|
||||
"secret": {
|
||||
"secretName": "mysecret"
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}]
|
||||
```
|
||||
|
||||
### Restrictions
|
||||
Secret volume sources are validated to ensure that the specified object
|
||||
reference actually points to an object of type `Secret`. Therefore, a secret
|
||||
needs to be created before any pods that depend on it.
|
||||
|
||||
Secret API objects reside in a namespace. They can only be referenced by pods
|
||||
in that same namespace.
|
||||
|
||||
Individual secrets are limited to 1MB in size. This is to discourage creation
|
||||
of very large secrets which would exhaust apiserver and kubelet memory.
|
||||
However, creation of many smaller secrets could also exhaust memory. More
|
||||
comprehensive limits on memory usage due to secrets is a planned feature.
|
||||
|
||||
### Consuming Secret Values
|
||||
|
||||
The program in a container is responsible for reading the secret(s) from the
|
||||
files. Currently, if a program expects a secret to be stored in an environment
|
||||
variable, then the user needs to modify the image to populate the environment
|
||||
variable from the file as an step before running the main program. Future
|
||||
versions of Kubernetes are expected to provide more automation for populating
|
||||
enviroment variables from files.
|
||||
|
||||
|
||||
## Changes to Secrets
|
||||
|
||||
Once a pod is created, its secret volumes will not change, even if the secret
|
||||
resource is modified. To change the secret used, the original pod must be
|
||||
deleted, and a new pod (perhaps with an identical PodSpec) must be created.
|
||||
Therefore, updating a secret follows the same workflow as deploying a new
|
||||
container image. The `kubectl rollingupdate` command can be used ([man
|
||||
page](kubectl-rollingupdate.md)).
|
||||
|
||||
The resourceVersion of the secret is not specified when it is referenced.
|
||||
Therefore, if a secret is updated at about the same time as pods are starting,
|
||||
then it is not defined which version of the secret will be used for the pod. It
|
||||
is not possible currently to check what resource version of a secret object was
|
||||
used when a pod was created. It is planned that pods will report this
|
||||
information, so that a controller could restart ones using a old
|
||||
resourceVersion. In the interim, if this is a concern, it is recommended to not
|
||||
update the data of existing secrets, but to create new ones with distinct names.
|
||||
|
||||
## Use cases
|
||||
|
||||
### Use-Case: Pod with ssh keys
|
||||
|
||||
To create a pod that uses an ssh key stored as a secret, we first need to create a secret:
|
||||
|
||||
```json
|
||||
{
|
||||
"apiVersion": "v1beta2",
|
||||
"kind": "Secret",
|
||||
"id": "ssh-key-secret",
|
||||
"data": {
|
||||
"id-rsa.pub": "dmFsdWUtMQ0K",
|
||||
"id-rsa": "dmFsdWUtMg0KDQo="
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** The serialized JSON and YAML values of secret data are encoded as
|
||||
base64 strings. Newlines are not valid within these strings and must be
|
||||
omitted.
|
||||
|
||||
Now we can create a pod which references the secret with the ssh key and
|
||||
consumes it in a volume:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "secret-test-pod",
|
||||
"kind": "Pod",
|
||||
"apiVersion":"v1beta2",
|
||||
"labels": {
|
||||
"name": "secret-test"
|
||||
},
|
||||
"desiredState": {
|
||||
"manifest": {
|
||||
"version": "v1beta1",
|
||||
"id": "secret-test-pod",
|
||||
"containers": [{
|
||||
"name": "ssh-test-container",
|
||||
"image": "mySshImage",
|
||||
"volumeMounts": [{
|
||||
"name": "secret-volume",
|
||||
"mountPath": "/etc/secret-volume",
|
||||
"readOnly": true
|
||||
}]
|
||||
}],
|
||||
"volumes": [{
|
||||
"name": "secret-volume",
|
||||
"source": {
|
||||
"secret": {
|
||||
"secretName": "ssh-key-secret"
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When the container's command runs, the pieces of the key will be available in:
|
||||
|
||||
/etc/secret-volume/id-rsa.pub
|
||||
/etc/secret-volume/id-rsa
|
||||
|
||||
The container is then free to use the secret data to establish an ssh connection.
|
||||
|
||||
### Use-Case: Pods with prod / test credentials
|
||||
|
||||
This example illustrates a pod which consumes a secret containing prod
|
||||
credentials and another pod which consumes a secret with test environment
|
||||
credentials.
|
||||
|
||||
The secrets:
|
||||
|
||||
```json
|
||||
[{
|
||||
"apiVersion": "v1beta2",
|
||||
"kind": "Secret",
|
||||
"id": "prod-db-secret",
|
||||
"data": {
|
||||
"username": "dmFsdWUtMQ0K",
|
||||
"password": "dmFsdWUtMg0KDQo="
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "v1beta2",
|
||||
"kind": "Secret",
|
||||
"id": "test-db-secret",
|
||||
"data": {
|
||||
"username": "dmFsdWUtMQ0K",
|
||||
"password": "dmFsdWUtMg0KDQo="
|
||||
}
|
||||
}]
|
||||
```
|
||||
|
||||
The pods:
|
||||
|
||||
```json
|
||||
[{
|
||||
"id": "prod-db-client-pod",
|
||||
"kind": "Pod",
|
||||
"apiVersion":"v1beta2",
|
||||
"labels": {
|
||||
"name": "prod-db-client"
|
||||
},
|
||||
"desiredState": {
|
||||
"manifest": {
|
||||
"version": "v1beta1",
|
||||
"id": "prod-db-pod",
|
||||
"containers": [{
|
||||
"name": "db-client-container",
|
||||
"image": "myClientImage",
|
||||
"volumeMounts": [{
|
||||
"name": "secret-volume",
|
||||
"mountPath": "/etc/secret-volume",
|
||||
"readOnly": true
|
||||
}]
|
||||
}],
|
||||
"volumes": [{
|
||||
"name": "secret-volume",
|
||||
"source": {
|
||||
"secret": {
|
||||
"secretName": "prod-db-secret"
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "test-db-client-pod",
|
||||
"kind": "Pod",
|
||||
"apiVersion":"v1beta2",
|
||||
"labels": {
|
||||
"name": "test-db-client"
|
||||
},
|
||||
"desiredState": {
|
||||
"manifest": {
|
||||
"version": "v1beta1",
|
||||
"id": "test-db-pod",
|
||||
"containers": [{
|
||||
"name": "db-client-container",
|
||||
"image": "myClientImage",
|
||||
"volumeMounts": [{
|
||||
"name": "secret-volume",
|
||||
"mountPath": "/etc/secret-volume",
|
||||
"readOnly": true
|
||||
}]
|
||||
}],
|
||||
"volumes": [{
|
||||
"name": "secret-volume",
|
||||
"source": {
|
||||
"secret": {
|
||||
"secretName": "test-db-secret"
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}]
|
||||
```
|
||||
|
||||
Both containers will have the following files present on their filesystems:
|
||||
```
|
||||
/etc/secret-volume/username
|
||||
/etc/secret-volume/password
|
||||
```
|
||||
|
||||
Note how the specs for the two pods differ only in one field; this facilitates
|
||||
creating pods with different capabilities from a common pod config template.
|
||||
|
||||
### Use-case: Secret visible to one container in a pod
|
||||
<a name="use-case-two-containers"></a>
|
||||
|
||||
Consider a program that needs to handle HTTP requests, do some complex business
|
||||
logic, and then sign some messages with an HMAC. Because it has complex
|
||||
application logic, there might be an unnoticed remote file reading exploit in
|
||||
the server, which could expose the private key to an attacker.
|
||||
|
||||
This could be divided into two processes in two containers: a frontend container
|
||||
which handles user interaction and business logic, but which cannot see the
|
||||
private key; and a signer container that can see the private key, and responds
|
||||
to simple signing requests from the frontend (e.g. over localhost networking).
|
||||
|
||||
With this partitioned approach, an attacker now has to trick the application
|
||||
server into doing something rather arbitrary, which may be harder than getting
|
||||
it to read a file.
|
||||
|
||||
## Security Properties
|
||||
|
||||
### Protections
|
||||
|
||||
Because `secret` objects can be created independently of the `pods` that use
|
||||
them, there is less risk of the secret being exposed during the workflow of
|
||||
creating, viewing, and editing pods. The system can also take additional
|
||||
precautions with `secret` objects, such as avoiding writing them to disk where
|
||||
possible.
|
||||
|
||||
A secret is only sent to a node if a pod on that node requires it. It is not
|
||||
written to disk. It is stored in a tmpfs. It is deleted once the pod that
|
||||
depends on it is deleted.
|
||||
|
||||
On most Kubernetes-project-maintained distributions, commuication between user
|
||||
to the apiserver, and from apiserver to the kubelets, is protected by SSL/TLS.
|
||||
Secrets are protected when transmitted over these channels.
|
||||
|
||||
There may be secrets for several pods on the same node. However, only the
|
||||
secrets that a pod requests are potentially visible within its containers.
|
||||
Therefore, one Pod does not have access to the secrets of another pod.
|
||||
|
||||
There may be several containers in a pod. However, each container in a pod has
|
||||
to request the secret volume in its `volumeMounts` for it to be visible within
|
||||
the container. This can be used to construct useful [security partitions at the
|
||||
Pod level](#use-case-two-containers).
|
||||
|
||||
### Risks
|
||||
|
||||
- Applications still need to protect the value of secret after reading it from the volume,
|
||||
such not accidentally logging it or transmitting it to an untrusted party.
|
||||
- A user who can create a pod that uses a secret can also see the value of that secret. Even
|
||||
if apiserver policy does not allow that user to read the secret object, the user could
|
||||
run a pod which exposes the secret.
|
||||
If multiple replicas of etcd are run, then the secrets will be shared between them.
|
||||
By default, etcd does not secure peer-to-peer communication with SSL/TLS, though this can be configured.
|
||||
- It is not possible currently to control which users of a kubernetes cluster can
|
||||
access a secret. Support for this is planned.
|
||||
- Currently, anyone with root on any node can read any secret from the apiserver,
|
||||
by impersonating the kubelet. It is a planned feature to only send secrets to
|
||||
nodes that actually require them, to restrict the impact of a root exploit on a
|
||||
single node.
|
||||
|
@ -34,6 +34,10 @@ kubernetes API, or to contribute directly to the kubernetes project.
|
||||
attached to objects, such as pods. Labels can be used to organize and to
|
||||
select subsets of objects.
|
||||
|
||||
* **Secrets** ([secrets.md](secrets.md)): A Secret stores sensitive data
|
||||
(e.g. ssh keys, passwords) separately from the Pods that use them, protecting
|
||||
the sensitive data from proliferation by tools that process pods.
|
||||
|
||||
* **Accessing the API** ([accessing_the_api.md](accessing_the_api.md)):
|
||||
Ports, IPs, proxies, and firewall rules.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user