Kubernetes Secrets Operations: Difference between revisions
(54 intermediate revisions by the same user not shown) | |||
Line 70: | Line 70: | ||
==From a Manifest== | ==From a Manifest== | ||
{{External|[https://kubernetes.io/docs/concepts/configuration/secret/#creating-a-secret-manually Creating a Secret Manually]}} | {{External|[https://kubernetes.io/docs/concepts/configuration/secret/#creating-a-secret-manually Creating a Secret Manually]}} | ||
The secret's [[Kubernetes Cluster Configuration Concepts#Secret_Data_Map|data map]] key/value pairs can be specified in the manifest. The value must be base64-encoded before being written in the manifest. | The secret's [[Kubernetes Cluster Configuration Concepts#Secret_Data_Map|data map]] key/value pairs can be specified in the manifest. The value must be base64-encoded before being written in the manifest. | ||
Line 85: | Line 84: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Alternatively, the values can be specified in clear as part of the "stringData" map; they will be encoded by Kubernetes when the secret is created: | The value for the "shape" key above was generated as follows: | ||
<syntaxhighlight lang='bash'> | |||
echo -n "square" | base64 | |||
</syntaxhighlight> | |||
Alternatively, the values can be specified in clear as part of the "[[Kubernetes Cluster Configuration Concepts#Secret_StringData_Map|stringData]]" map; they will be encoded by Kubernetes when the secret is created: | |||
<syntaxhighlight lang='yaml'> | <syntaxhighlight lang='yaml'> | ||
Line 96: | Line 101: | ||
shape: square | shape: square | ||
size: large | size: large | ||
</syntaxhighlight> | |||
Assuming that the secret was declared into a blue-secret.yaml file, it can be deployed as follows: | |||
<syntaxhighlight lang='bash'> | |||
kubectl apply -f ./blue-secret.yaml | |||
</syntaxhighlight> | |||
It then can be queried with: | |||
<syntaxhighlight lang='bash'> | |||
kubectl -o yaml get secret blue | |||
</syntaxhighlight> | |||
<syntaxhighlight lang='yaml'> | |||
apiVersion: v1 | |||
data: | |||
shape: c3F1YXJl | |||
size: bGFyZ2U= | |||
kind: Secret | |||
metadata: | |||
annotations: | |||
kubectl.kubernetes.io/last-applied-configuration: | | |||
{"apiVersion":"v1","data":{"shape":"c3F1YXJl","size":"bGFyZ2U="},"kind":"Secret","metadata":{"annotations":{},"name":"blue","namespace":"default"},"type":"Opaque"} | |||
creationTimestamp: "2020-01-29T18:37:40Z" | |||
name: blue | |||
namespace: default | |||
resourceVersion: "3993231" | |||
selfLink: /api/v1/namespaces/default/secrets/blue | |||
uid: 6def1f72-42c6-11ea-87aa-025000000001 | |||
type: Opaque | |||
</syntaxhighlight> | |||
For more details on secret manifests, see: {{Internal|Kubernetes Secret Manifest#Example|Secret Manifest}} | |||
===Use Cases=== | |||
====Secret Containing a YAML Tree==== | |||
<syntaxhighlight lang='yaml'> | |||
kind: Secret | |||
... | |||
stringData: | |||
myFile.yaml: | | |||
something: 'a' | |||
somethingElse: | |||
b: 'c' | |||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 101: | Line 147: | ||
{{External|[https://kubernetes.io/docs/concepts/configuration/secret/#creating-a-secret-from-generator Creating a Secret from Generator]}} | {{External|[https://kubernetes.io/docs/concepts/configuration/secret/#creating-a-secret-from-generator Creating a Secret from Generator]}} | ||
<font color=darkgray>TODO</font> | <font color=darkgray>TODO</font> | ||
=Consume a Secret= | |||
==Consume a Secret as a File== | |||
{{External|[https://github.com/ovidiuf/playground/tree/master/kubernetes/secrets/project-secret-as-file Playground - Consume a Secret as File]}} | |||
{{External|https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.16/#secretprojection-v1-core}} | |||
Declare and deploy the "blue" secret as described in the [[#From_a_Manifest|Create a Secret from a Manifest]] section. | |||
Modify the pod to add a new <code>.spec.volumes[]</code> volume, whose <code>.spec.volumes[].secret.secretName</code> must match the name of the secret to be exposed to the pod. Then under the container's <code>volumeMounts</code> add a volume mount whose name is the name of the <code>secret</code> volume. Specify <code>spec.containers[].volumeMounts[].readOnly</code> to be <code>true</code>. <font color=darkgray>Even if I don't specify readOnly=true, when trying to edit the content of the file I get "Warning: Changing a readonly file".</font> If there are multiple containers in the pod, each container will need its own volumeMounts mount, but only one <code>.spec.volumes[]</code> volume is needed per pod. | |||
When the pod deploys, each key/value entry of the secret's [[Kubernetes Cluster Configuration Concepts#Secret_Data_Map|data map]] is exposed as an individual file in the directory specified by <code>spec.containers[].volumeMounts.mountPath</code>, "/etc/blue" in this case. Each individual file name is given by the key and the file content is filled with decoded value from the secret. Note that if the directory exists in the container image, its original content is "hidden" by the mount and it becomes inaccessible. | |||
In the example below, assuming that the "blue" secret has a key named "shape" with a "large" value, the pod sees a /etc/blue/shape file and the content of the file is "large" text: | |||
<syntaxhighlight lang='yaml'> | |||
kind: Pod | |||
... | |||
spec: | |||
containers: | |||
- name: ... | |||
volumeMounts: | |||
- name: secret-volume | |||
mountPath: /etc/blue | |||
readOnly: true | |||
volumes: | |||
- name: secret-volume | |||
secret: | |||
secretName: blue | |||
defaultMode: 0400 | |||
</syntaxhighlight> | |||
Also see: {{Internal|Kubernetes_Pod_Manifest#volumeMounts|Pod Manifest - volumeMounts}} | |||
===Projection of Keys to Specific Paths with Specific Permissions=== | |||
By default, all keys of the map are projected in the root of the volume corresponding to the secret. However, individual keys can be mapped on arbitrary relative paths by using spec.volumes[].secret.items[].key[]. Not only an arbitrary path, but also arbitrary permissions under which the file will be exposed can be specified: | |||
<syntaxhighlight lang='yaml'> | |||
kind: Pod | |||
... | |||
spec: | |||
... | |||
volumes: | |||
- name: secret-volume | |||
secret: | |||
secretName: blue | |||
defaultMode: 0400 | |||
items: | |||
- key: shape | |||
path: dir-A/dir-B/secret-shape | |||
mode: 0440 | |||
</syntaxhighlight> | |||
If the secret contains multiple keys, and the "items[]" element is used, only the keys specified under "items:" will be projected, and the rest will be ignored. Also, if a key that does not exist in the secret is specified, the volume '''is not created'''. | |||
==Consume a Secret as an Environment Variable== | |||
<syntaxhighlight lang='yaml'> | |||
kind: Pod | |||
... | |||
spec: | |||
containers: | |||
- name: ... | |||
volumeMounts: | |||
- name: secret-volume | |||
env: | |||
- name: SECRET_SHAPE | |||
valueFrom: | |||
secretKeyRef: | |||
name: blue | |||
key: shape | |||
~ | |||
</syntaxhighlight> | |||
==Project a Secret Key to a Specific Path== | |||
<font color=darkgray>TODO: [https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets-as-files-from-a-pod Projection of secret keys to specific paths] Also addresses secret file permissions.</font> | |||
=Update a Secret= | |||
==With an Updated Manifest== | |||
The manifest can be updated and applied with [[kubectl apply]], the state will be merged and updated. Some of its file projections will be updated after a while. Empty string is a valid update. For more details see:{{Internal|Kubernetes_Cluster_Configuration_Concepts#File_Projection_on_Secret_Update|File Projection on Secret Update}} | |||
==Interactively== | |||
kubectl edit secret blue | |||
and then edit the values of the fields to be updated. Note that a base64-encoded string is needed. | |||
=Delete a Secret= | |||
kubectl delete secret blue | |||
More details on what happens if a secret is deleted: {{Internal|Kubernetes_Cluster_Configuration_Concepts#Secret_Details|Secret Details}} |
Latest revision as of 05:40, 30 October 2020
Internal
Inspecting Secrets
kubectl get secrets
kubectl get secret mysecret -o yaml
The value of the secret is base64-encoded and it can be retrieved with:
echo '....' | base64 --decode
kubectl describe secret secret-name
Create a Secret
With kubectl CLI
From File
Declare the secret content in one (or more) file(s) on the local filesystem. The file name will become a secret's data map key. Multiple files can be added to the same secret. When the secret is exposed to a pod, the content will be available as volume files with the same name.
echo -n "test-user" > ./username.txt echo -p "test-password" > ./password.txt
kubectl create secret generic username-and-password --from-file=./username.txt --from-file=./password.txt
This will create the following secret:
Name: username-and-password
Namespace: test
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password.txt: 17 bytes
username.txt: 9 bytes
From Literal
The secret's data map key followed by "=" followed by value can be specified on command line with --from-literal=.
kubectl create secret generic red --from-literal=key1=somevalue --from-literal=key2=someothervalue
From Env File
A key-value (env) file can be used as source for secrets with --from-env-file=. The keys in the key-value file become the keys in the secret's data map.
kubectl create secret generic green --from-env-file=./test.txt
where test.txt:
key1=value1
key2=value2
Special Character Handling
Special characters such as '$', '*' and '!' require escaping (\).
From a Manifest
The secret's data map key/value pairs can be specified in the manifest. The value must be base64-encoded before being written in the manifest.
apiVersion: v1
kind: Secret
metadata:
name: blue
type: Opaque
data:
shape: c3F1YXJl
size: bGFyZ2U=
The value for the "shape" key above was generated as follows:
echo -n "square" | base64
Alternatively, the values can be specified in clear as part of the "stringData" map; they will be encoded by Kubernetes when the secret is created:
apiVersion: v1
kind: Secret
metadata:
name: blue
type: Opaque
stringData:
shape: square
size: large
Assuming that the secret was declared into a blue-secret.yaml file, it can be deployed as follows:
kubectl apply -f ./blue-secret.yaml
It then can be queried with:
kubectl -o yaml get secret blue
apiVersion: v1
data:
shape: c3F1YXJl
size: bGFyZ2U=
kind: Secret
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","data":{"shape":"c3F1YXJl","size":"bGFyZ2U="},"kind":"Secret","metadata":{"annotations":{},"name":"blue","namespace":"default"},"type":"Opaque"}
creationTimestamp: "2020-01-29T18:37:40Z"
name: blue
namespace: default
resourceVersion: "3993231"
selfLink: /api/v1/namespaces/default/secrets/blue
uid: 6def1f72-42c6-11ea-87aa-025000000001
type: Opaque
For more details on secret manifests, see:
Use Cases
Secret Containing a YAML Tree
kind: Secret
...
stringData:
myFile.yaml: |
something: 'a'
somethingElse:
b: 'c'
Creating Secrets with a Generator
TODO
Consume a Secret
Consume a Secret as a File
Declare and deploy the "blue" secret as described in the Create a Secret from a Manifest section.
Modify the pod to add a new .spec.volumes[]
volume, whose .spec.volumes[].secret.secretName
must match the name of the secret to be exposed to the pod. Then under the container's volumeMounts
add a volume mount whose name is the name of the secret
volume. Specify spec.containers[].volumeMounts[].readOnly
to be true
. Even if I don't specify readOnly=true, when trying to edit the content of the file I get "Warning: Changing a readonly file". If there are multiple containers in the pod, each container will need its own volumeMounts mount, but only one .spec.volumes[]
volume is needed per pod.
When the pod deploys, each key/value entry of the secret's data map is exposed as an individual file in the directory specified by spec.containers[].volumeMounts.mountPath
, "/etc/blue" in this case. Each individual file name is given by the key and the file content is filled with decoded value from the secret. Note that if the directory exists in the container image, its original content is "hidden" by the mount and it becomes inaccessible.
In the example below, assuming that the "blue" secret has a key named "shape" with a "large" value, the pod sees a /etc/blue/shape file and the content of the file is "large" text:
kind: Pod
...
spec:
containers:
- name: ...
volumeMounts:
- name: secret-volume
mountPath: /etc/blue
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: blue
defaultMode: 0400
Also see:
Projection of Keys to Specific Paths with Specific Permissions
By default, all keys of the map are projected in the root of the volume corresponding to the secret. However, individual keys can be mapped on arbitrary relative paths by using spec.volumes[].secret.items[].key[]. Not only an arbitrary path, but also arbitrary permissions under which the file will be exposed can be specified:
kind: Pod
...
spec:
...
volumes:
- name: secret-volume
secret:
secretName: blue
defaultMode: 0400
items:
- key: shape
path: dir-A/dir-B/secret-shape
mode: 0440
If the secret contains multiple keys, and the "items[]" element is used, only the keys specified under "items:" will be projected, and the rest will be ignored. Also, if a key that does not exist in the secret is specified, the volume is not created.
Consume a Secret as an Environment Variable
kind: Pod
...
spec:
containers:
- name: ...
volumeMounts:
- name: secret-volume
env:
- name: SECRET_SHAPE
valueFrom:
secretKeyRef:
name: blue
key: shape
~
Project a Secret Key to a Specific Path
TODO: Projection of secret keys to specific paths Also addresses secret file permissions.
Update a Secret
With an Updated Manifest
The manifest can be updated and applied with kubectl apply, the state will be merged and updated. Some of its file projections will be updated after a while. Empty string is a valid update. For more details see:
Interactively
kubectl edit secret blue
and then edit the values of the fields to be updated. Note that a base64-encoded string is needed.
Delete a Secret
kubectl delete secret blue
More details on what happens if a secret is deleted: