Kubernetes: modify a secret using kubectl? - kubernetes

How can I modify the values in a Kubernetes secret using kubectl?
I created the secret with kubernetes create secret generic, but there does not seem to be a way to modify a secret. For example, to add a new secret-value to it, or to change a secret-value in it.
I assume i can go 'low-level', and write the yaml-file and do a kubectl edit but I hope there is a simpler way.
(I'm using kubernetes 1.2.x)

The most direct (and interactive) way should be to execute kubectl edit secret <my secret>. Run kubectl get secrets if you'd like to see the list of secrets managed by Kubernetes.

In case you prefer a non-interactive update, this is one way of doing it:
kubectl get secret mysecret -o json | jq '.data["foo"]="YmFy"' | kubectl apply -f -
Note that YmFy is a base64-encoded bar string. If you want to pass the value as an argument, jq allows you to do that:
kubectl get secret mysecret -o json | jq --arg foo "$(echo bar | base64)" '.data["foo"]=$foo' | kubectl apply -f -
I'm more comfortable using jq but yq should also do the job if you prefer yaml format.

As I found myself in the need of modifying a secret, I landed up here.
Here is the most convenient way I found for editing a (one-line) secret.
This elaborates on kubectl edit secret <my secret> of Timo Reimann above.
kubectl edit secret <my secret> will (in my case) invoke vi.
Now I move the cursor to the space after the colon of the secret I want to edit.
Then I press r and [enter] which will put the base64 encoded value onto a line of its own.
Now I enter :. ! base64 -D which will decode the current line.
After making my changes to the value, I enter :. ! base64 which will encode the changed value.
Pressing k [shift]J will rejoin the secret name and its new value.
:wq will write the new secretfile and quit vi.
P.S. If the secret has a multi-line value, switch on line numbers (:set nu) and, after changing the decoded value, use A,B ! base64 where A and B are the line numbers of the first and last line of the value.
P.P.S I just learned the hard way that base64 will receive the text to encode with an appended newline :( If this is no issue for your values - fine. Otherwise my current solution is to filter this out with: .!perl -pe chomp | base64

Deriving from 'Skeeves' answer:
Base64 encode your value:
echo -n 'encode_My_Password' | base64
Open the secret in edit mode:
kubectl edit secret my-secret
The default editor will open, replace the value of an exiting key or add a new line and a new key with the encoded value.
Save and close the file. The updated value or new key-value pair has now been added to the secret.

The easiest way from the command line:
echo "This is my secret" | base64 | read output;kubectl patch secret my_secret_name -p="{\"data\":{\"secret_key\": \"$output\"}}" -v=1
It will encode value This is my secret and update your my_secret_name secret by adding secret_key key and encoded values as a last key-value pair in that secret.

I implemented a kubectl plugin just for this.
To install using krew
kubectl krew update
kubectl krew install modify-secret
To run it
kubectl modify-secret xyz -n kube-system
Demo

The Easy Way : Delete and recreate the secret
After looking at all these answers, for my needs the best solution was to delete and recreate :
kubectl delete secret generic
kubectl create secret generic # or whatever ..
If you want to do it the hard way :
Using edit to change a docker-registry secret
I came to this question looking to modify a "docker-registry" style secret.
Simply editing it using kubectl edit secret seemed fraught as I didn't know what the secret value looked like.
I had created it with a command like kubectl create secret docker-registry generic-registry-secret --docker-server=docker.server --docker-username='my-cloud-usernname' --docker-password='my-auth-token' --docker-email='my#email.com'
I could have edited it, I figured out after looking at the other various answers here how that could be done - I'm including my notes here in case they help others.
List secrets : kubectl get secrets
Details of specific secret : kubectl describe secrets/generic-registry-secret
Get value of secret : kubectl get secret generic-registry-secret -o jsonpath={.data}
Decode secret value : First get everything between "map[.dockerconfigjson:" and "]" and then do :
echo "x9ey_the_secret_encoded_value_here_X0b3=" | base64 --decode
I could then take from that the specific auth token value I was seeking, and replace it with a new one. And then run that new full entire string through a | base 64 to get the base 64 encoding, and now I could finally, confidently, change the value by using kubectl edit secret generic-registry-secret and put in the new correct value.
But a delete and recreate is the simpler option.
References :
https://kubernetes.io/docs/concepts/configuration/secret/
https://kubernetes.io/docs/tasks/configmap-secret/managing-secret-using-kubectl/

Add a new key to an existing secret.
kubectl patch secret $SECRET_NAME --type=json \
-p='[{
"op" : "add" ,
"path" : "/data/'$KEY'" ,
"value" : "'$(base64 <<< "$VALUE")'"
}]'
Update an existing key in a secret
kubectl patch secret $SECRET_NAME --type=json \
-p='[{
"op" : "replace" ,
"path" : "/data/'$KEY'" ,
"value" : "'$(base64 <<< "$VALUE")'"
}]'
I was only able to find the replace operation in documentation, with no mention of the add operation. However, it looked like it was RFC 6902 compliant, so I tested with add and it works fine. I would expect other operations defined by RFC 6902 to work as well, though I haven't tested them.

The fastest way I found:
# You need a version of micro that includes this commit https://github.com/zyedidia/micro/commit/9e8d76f2fa91463be660737d1de3bff61258c90d
kubectl get secrets my-secret -o json | jq -r .data.config | base64 -d | micro | base64 -w 0 | xclip -selection clipboard && kubectl edit secrets my-secret
And using a bash function that you can put in your profile:
function ks-edit { kubectl -n $1 get secrets $2 -o json | jq -r '.data."'$3'"' | base64 -d | micro | base64 -w 0 | xclip -selection clipboard && kubectl -n $1 edit secrets $2; }
You can call it like this:
ks-edit <namespace> <secret> <key>

Before editing secrets with kubectl...
I would highly recommend on using k9s (not only for this purpose, but also as a lightweight k8s CLI management tool).
As you can see below (ignore all white rectangles), when your cluster's context is set on terminal you just type k9s and you will hit a nice terminal where you can inspect all cluster resources.
Just type ":" and enter the resource name (secrets in this case) which will appear in the middle of screen.
Then you can choose a secret with the up and down arrows and type e to edit it (green arrow):

By far the easiest way to do this is to mantain a local .env file for each of your secrets.
e.g
MY_SECRET=something
PASSWORD=anotherthing
Just run
kubectl create secret generic <name> --from-env-file=.env
And when you need to change it - just delete it and run the above command again.
No messing with base64

Always get the copy of secrets before editing it -
kubectl get secrets <your-secret-name> -n <namespace> -o yaml > mysecret.yaml
Now you can edit run edit command to edit your secret -
kubectl edit secrets <your-secret-name> -n <namespace>
or you can make copy of your mysecret.yaml file & exit the secrets inside that & run -
kubectl apply -f mysecret.yaml
Make sure you are decoding & encoding with base64 for viewing & adding secrets respectively.

Here's my one liner:
$ kubectl get secrets/my-secret -o yaml | yq '.dataStrings = (.data | map_values(#base64d)) | del(.data)' | vipe | yq '.data = (.dataStrings | map_values(#base64)) | del(.dataStrings)' | kubectl apply -f -

In case you're wondering how to do this with k9s, I am adding here instructions on how to do this step by step:
Install krew from here https://krew.sigs.k8s.io/docs/user-guide/setup/install/ (skip this step in case you have already it)
Install modify-secret plugin:
kubectl krew install modify-secret
Run the following command or add it to ~/.zshrc or ~/.bashrc:
export XDG_CONFIG_HOME=~/
Add the following to ~/k9s/plugin.yml
plugin:
edit-secret:
shortCut: Ctrl-X
confirm: false
description: "Edit Decoded Secret"
scopes:
- secrets
command: kubectl
background: false
args:
- modify-secret
- --namespace
- $NAMESPACE
- --context
- $CONTEXT
- $NAME

Related

Create multiple k8s secrets ad hoc

With the following command: kubectl -n ns create secret generic test2 --from-literal=username=xxxxxx I am able to create secrets. I would like to find a way (programmatically) to use a list instead of using literal=username=xxxxxx because sometimes I need more than one literal.
What I tried is the following:
NS=test-ns
SF=secrets.yaml
unset SECRET_NAMES
value+=(username)
value+=(password)
echo "" > $SF
for value in "${value[#]}"
do
kubectl -n ${NS} create secret generic test2 \
--from-literal=$value=xxxxxx \
-o yaml >${NS}-$secret_name.yaml
done
cat $SF
but it failed because it creates first the username and then it not able to create the password because the secret test2 is already exits. Do you have any idea how can I solve this issue?
You can repeat the --from-literal flag as many times as you want:
$ kubectl create secret generic test2 --from-literal=username=xxxxxx --from-literal=username2=xxxxxx
secret/test2 created
$ kubectl get secrets test2 -oyaml
apiVersion: v1
type: Opaque
kind: Secret
metadata:
name: test2
data:
username: eHh4eHh4
username2: eHh4eHh4
Addressing your comment on #whites11 answer as quoted here:
Yes but this will not work in my case. The thing is that I have created a jenkins job and is parametrized , and I don't know each user how many literal will create.
First, you have to determine how you want your user to specify the literals in the Jenkin job parameter, for example using '=' symbol:
username=user1
password=p#ssword
realm=app
timezone=pdt
Let's say the job multi-line string parameter is passed to the job script as $SECRETS. Here is how you loop against the literals and construct the needed kubectl command:
ARGS=()
while read LINE
do
if [[ "${LINE}" != *"="* ]]; then continue; fi
ARGS+=( "--from-literal=${LINE%%=*}=${LINE#*=}" )
done <<< "$SECRETS"
Now that you have the arguments to kubectl create configmap command in ARGS array variable, you can use it like below:
NS=test-ns
kubectl -n ${NS} create secret generic test2 "${ARGS[#]}"

How to copy kubernetes one secrets value to another secretes within same namespace

I am using kubernetes and its resources like secrets. During deployment one secret has been created (say test-secret) with some values inside it.
Now I need to renamed this secretes (dev-secret) within the same namespace.
How can I rename the secret or how can I copy test-secret value to dev-secret.
Please let me know the correct approach for this.
There is no specific way to do this. The Kubernetes API does not have "rename" as an operation. In this particular case you would kubectl get server test-secret -o yaml, clean up the metadata: sections that don't apply anymore, edit the name, and kubectl apply it again.
Extending #coderanger answer:
If you still have secret config yaml file you can do
kubectl delete -f </path/to/secret-config-yaml>
change metadata.name object and issue
kubectl apply -f </path/to/secret-config-yaml>
I needed to do something similar: rename K8s secrets.
I searched everywhere, but could not find a good way to do it.
So I wrote a bash script for copying secrets into new secrets with a new name.
In my case, I also wanted to do this in batch, as I had many secrets with the same prefix that I needed to change.
I don't work with bash all the time, so there might be better ways... but it did the trick for me.
I hope it helps!
#!/bin/bash
# Copies K8s secrets with names containing the NAME_PART into new
# secrets where the NAME_PART was replaced with NEW_NAME_PART.
# i.e. if NAME_PART is "test-abc" and NEW_NAME_PART is "test-xyz", a secret names test-abc-123
# will be copied into a new secret named test-xyz-123
#
# Pre-requisites:
# - have kubectl installed and pointing to the cluster you want to alter
#
# NOTE: tested with kubectl v1.18.0 and K8s v1.21.5-eks-bc4871b
# configure the NAME_PARTs here
NAME_PART=test-abc
NEW_NAME_PART=test-xyz
WORK_DIR=work_secret_copy
mkdir -p $WORK_DIR
echo "Getting secrets from K8s..."
allSecrets=`kubectl get secrets | tail -n +2 | cut -d " " -f1`
matchingSecrets=`echo $allSecrets | tr ' ' '\n' | grep $NAME_PART`
#printf "All secrets:\n $allSecrets \n"
#printf "Secrets:\n $secrets \n"
for secret in $matchingSecrets; do
newSecret=${secret/$NAME_PART/$NEW_NAME_PART}
echo "Copying secret $secret to $newSecret"
# skip this secret if one with the new name already exists
if [[ $(echo $allSecrets | tr ' ' '\n' | grep -e "^$newSecret\$") ]]; then
echo "Secret $newSecret already exists, skipping..."
continue
fi
kubectl get secret $secret -o yaml \
| grep -v uid: \
| grep -v time: \
| grep -v creationTimestamp: \
| sed "s/$secret/$newSecret/g" \
> $WORK_DIR/$newSecret.yml
kubectl apply -f $WORK_DIR/$newSecret.yml
done

How to replace JSON value in kubectl output using go-template?

I have a configMap and I want to create a backup configMap by using the last applied configuration from that.
I use the following command to get the last applied configuration:
kubectl get cm app-map -n app-space \
-o go-template \
--template='{{index .metadata "annotations" "kubectl.kubernetes.io/last-applied-configuration"}}' > backup.json
It returns something like this [the content of backup.json]:
{"kind":"ConfigMap","apiVersion":"v1","metadata":{"name":"app-map","creationTimestamp":null},"data":{"app.yml":"xxxxxxxxx","config.yml":"yyyyyyyyy"}}
Now, I want my backup configMap to have a different name. So, I want to change the .metadata.name from app-map to app-map-backup.
Is there a way I can achieve that with kubectl and -o go-template? I want to have the name changed before I write it to the backup.json file.
I know I can do that using jq but I do not have permission to install jq on the server where I am using kubectl.
you could use kubectl bulk plugin. The below command will replicate your config map
# get resource(s) and create with field(name) change
kubectl bulk configmap app-map -n app-space create name app-mapp-backup
Kubectl bulk is very powerful to use, I suggest to check samples.
You cannot do this just using kubectl. But there are other ways.
You can download statically linked jq binary from official jq website:
wget https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64
chmod +x jq-linux64
and then you can use this binary like following:
kubectl -o go-template [...] | ./jq-linux64 ...
or you can use sed:
kubectl -o go-template [...] | sed 's/"name":"app-map"/"name":"app-map-backup"/'

kubectl: get specific value from a secret in plaintext

I want to get the value of a specific field of a secret in a shell script.
From the kubectl get secret documentation, it seems the standard way to get a secret returns the whole thing, in a specified format, with the values base64 encoded.
So, to get the bar field of the foo secret, output as an unencoded string, I'm doing this:
kubectl get secret foo -o json | jq -r ".data.bar" | base64 --decode
That is
get the whole foo secret as JSON
pipe to jq to read the bar field from the JSON
decode the value using base64
Is there a way to do this only using kubectl?
Or an elegant way in POSIX-compliant shell that doesn't rely on any dependencies like jq?
Try this
kubectl get secret foo --template={{.data.bar}} | base64 --decode
No need of jq.
In cases when key contains dots:
apiVersion: v1
metadata:
name: foo
data:
bar.baz: RnVja2VkIFVwIEJleW9uZCBBbGwgUmVjb2duaXRpb24=
the syntax would be:
kubectl get secret foo -o jsonpath="{.data['bar\.baz']}" | base64 -d
This should work since Kubernetes 1.11 (see PR 60755):
kubectl get secret foo -o go-template='{{ .data.bar | base64decode }}'
kubectl get secret foo -o jsonpath={.data.bar} | base64 --decode
https://kubernetes.io/docs/reference/kubectl/jsonpath/
You can try the following command, it will decode all the values in the secret.
kubectl get secret <secret-name> -o json | jq '.data | map_values(#base64d)'
For a hyphenated key, here's the escaping trick to get the decoded value directly from kubectl:
kubectl get secret foo -o go-template='{{ index .data \"bar-baz\" | base64decode }}'
(Tested with kubectl 1.21.5)
For those of you guys coming here via Google and want to do it from PowerShell, where base64 ist not available, use the following:
[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($(kubectl get secret foo -o jsonpath='{.data.bar}')))

Kubernetes / kubectl print all secrets

I would like to use kubectl to print out all key-value pairs in my Secrets. I cannot figure out how to do this in one line with the -o --jsonpath flag or by piping into jq. I could certainly make a script to do this but I feel there must be a better way, given that the kubernetes GUI is pretty straightforward and liberal when it comes to letting you view Secrets.
Say I create secret like so:
kubectl create secret generic testsecret --from-literal=key1=val1 --from-literal=key2=val2
Now I can run kubectl get secret testsecret -o json to get something like:
{
"apiVersion": "v1",
"data": {
"key1": "dmFsMQ==",
"key2": "dmFsMg=="
},
...
}
I can do something like
kubectl get secret testsecret -o jsonpath='{.data}'
or
kubectl get secret testsecret -o json | jq '.data'
to get my key-value pairs in non-list format then I'd have to base64 --decode the values.
What is the easiest way to get a clean list of all my key-value pairs? Bonus points for doing this across all Secrets (as opposed to just one specific one, as I did here).
Sufficiently recent versions of jq have a filter for decoding base64 but it can only be used if the value that was encoded is a valid JSON string.
Anyway, you could start by trying:
.data | map_values(#base64d)
I read this question as asking for how to decode all secrets in one go. I built on the accepted answer to produce a one-liner to do this:
kubectl get secrets -o json | jq '.items[] | {name: .metadata.name,data: .data|map_values(#base64d)}'
This has the added benefit of listing the name of the secret along with the decoded values for readability.
If you need to extract tls certificates and/or keys from a kubernetes secret and you have an older jq version not supporting map_values(#base64d):
kubectl get secrets tls-cert -o json | jq '.data' | cut -d '"' -f 4 | tr -d '{}' | base64 --decode
When using the accepted answer you may come across exception
jq: error (at <stdin>:96): Cannot iterate over null (null)
This might be because some json might not be fully formed, use an additional filter
kubectl get secrets -o json | jq '.items[] | select(null != .data) | {name: .metadata.name,data: .data|map_values(#base64d)}'
The above will ensure to produce expected results