176

I've created a secret using

kubectl create secret generic production-tls \
  --from-file=./tls.key \
  --from-file=./tls.crt

If I'd like to update the values - how can I do this?

Mike
  • 1,080
  • 1
  • 9
  • 25
Chris Stryczynski
  • 30,145
  • 48
  • 175
  • 286

9 Answers9

388

This should work:

kubectl create secret generic production-tls \
--save-config \
--dry-run=client \
--from-file=./tls.key --from-file=./tls.crt \
-o yaml | \
kubectl apply -f -
Jonathan
  • 5,495
  • 4
  • 38
  • 53
Janos Lenart
  • 25,074
  • 5
  • 73
  • 75
  • 14
    In the latest version of k8s, you'll need to provide `--save-config` to the `kubectl create secret` in order to avoid a CLI warning. – David House Oct 20 '18 at 18:42
  • 6
    fyi, recent (Sept 2019) syntax that worked for tls secret: ```kubectl create secret tls my-domain-tls --namespace=default --key=./tls.key --cert=./tls.crt --dry-run -o yaml | kubectl apply -f -``` The certs were in plain text. – ldg Sep 12 '19 at 23:47
  • 5
    Need to use `--dry-run=client` with kubectl 1.18 or higher. – RichVel Sep 16 '20 at 10:00
  • This will create "kubectl.kubernetes.io/last-applied-configuration" with no encrypted values – SergkeiM May 18 '22 at 12:44
  • 2
    @Froxz, the values in a Secret are not encrypted; they are simply base64 encoded. There is no change in security from using this command. – Janos Lenart May 20 '22 at 15:09
115

You can delete and immediately recreate the secret:

kubectl delete secret production-tls \
--ignore-not-found

kubectl create secret generic production-tls \
--from-file=./tls.key \
--from-file=./tls.crt

I put these commands in a script. The --ignore-not-found prevents getting a warning on the first run.

Mike
  • 1,080
  • 1
  • 9
  • 25
P.J.Meisch
  • 18,013
  • 6
  • 50
  • 66
  • 6
    what happens with pods while the secret is deleted? – Bruno Medeiros Feb 17 '19 at 15:05
  • 7
    @BrunoJCM running pods are not affected, no matter wether they get the secrets via env variables or mounted as volumes. If a pod i started in the time while there is no secret, they run into an error; therefore Janos' answer is the preferred way to go. – P.J.Meisch Feb 18 '19 at 17:55
  • 2
    Yes, I see, using `apply` makes much more sense, thanks! – Bruno Medeiros Feb 19 '19 at 05:40
  • This was not working for me because I forgot the `--namespace=kube-system` – Souradeep Nanda Jun 11 '19 at 05:44
  • 2
    depends on what namespace you want to add the secret to, if not _default_ than of course you have to add the namespace argument. – P.J.Meisch Jun 11 '19 at 06:42
  • I use this approach in CI scripts. But it produce race condition when several jobs run simultaneous and one script failed in case secret was deleted and not yet created. Be careful! – Panoptik Nov 20 '20 at 08:52
  • That solution is not equivalent to an update, as requested by the OP. Deleting the secret means losing all metadata in the secret, as well as any other field in the secret. – Denilson Jan 06 '23 at 16:20
14

Alternatively, you can also use jq's = or |= operator to update secrets on the fly.

TLS_KEY=$(base64 < "./tls.key" | tr -d '\n')
TLS_CRT=$(base64 < "./tls.crt" | tr -d '\n')
kubectl get secrets production-tls -o json \
        | jq '.data["tls.key"] |= "$TLS_KEY"' \
        | jq '.data["tls.crt"] |= "$TLS_CRT"' \
        | kubectl apply -f -

Although it might not be as elegant or simple as the kubectl create secret generic --dry-run approach, technically, this approach is truly updating values rather than deleting/recreating them. You'll also need jq and base64 (or openssl enc -base64) commands available, tr is a commonly-available Linux utility for trimming trailing newlines.

See here for more details about jq update operator |=.

Devy
  • 9,655
  • 8
  • 61
  • 59
  • 1
    Can you just add `-w0` to disable line wrapping in the `base64` command? https://linux.die.net/man/1/base64 – patricknelson Dec 16 '20 at 03:51
  • @chunk_split no. I am using macOS, the BSD version of the base64 command doesn't have that optioin. – Devy Dec 22 '20 at 14:03
  • Conceptually, assigning the values to variables increases the chances of them getting leaked into a log file when someone turns debugging on with `set -x`. On a more practical level, since the jq arguments are between single quotes, the shell variables are not going to be expanded. Therefore, the final secret will have the literal characters $TLS_KEY and $TLS_CRT instead of their actual values. You would need to do something like: jq --arg TLS_KEY $TLS_KEY '.data["tls.key"] |= "$TLS_KEY"' – Denilson Jan 06 '23 at 16:30
13

As I wasn't able to reply to Devy's answer above, which I like because it will preserve Ownership where deleting and recreating has the potential to lose any extra information in the record. I'm adding this for the newer people who may not immediately understand whey their variables aren't being interpolated.

TLS_KEY=$(base64 < "./tls.key" | tr -d '\n')
TLS_CRT=$(base64 < "./tls.crt" | tr -d '\n')
kubectl get secrets production-tls -o json \
        | jq ".data[\"tls.key\"] |= \"$TLS_KEY\"" \
        | jq ".data[\"tls.crt\"] |= \"$TLS_CRT\"" \
        | kubectl apply -f -

This lead me to attempting to use the 'patch' method of kubectl, which also seems to work.

kubectl \
        patch \
        secret \
        production-tls \
        -p "{\"data\":{\"tls.key\":\"${TLS_KEY}\",\"tls.crt\":\"${TLS_CRT}\"}}"

Thanks Devy for the answer that best met my needs.

CJ Maahs
  • 146
  • 1
  • 4
4

Late to the party but still here are my inputs.

Potentially we can use both the patch or edit option.

  • With edit :

    kubectl edit secrets/<SECRET_NAME> -n <NAME_SPACE>
    
    • NOT the recommended way of editing the secrets.
    • You should have enough permission to do the above edit.
    • The value should be base64 encode by yourself and then the encoded value should be placed while editing.
  • With patch :

Preetham
  • 577
  • 5
  • 13
3

For more specific cases you might need to specify your namespace that the cert needs to be renewed and delete the old one.

For deletion of the cert

kubectl delete secret -n `namespace`

For the creation of new cert to specific namespace

kubectl create secret {your-cert-name} --key /etc/certs/{name}.com.key --cert /etc/certs/{name}.com.crt -n {namespace}
Preetham
  • 577
  • 5
  • 13
JohnBegood
  • 652
  • 1
  • 7
  • 8
2

Just to expand on these answers I found that adding '--ignore-not-found' to the delete helped with our CICD as it wouldn't error out if the secret didn't exist, it would just go ahead and create it:

kubectl delete secret production-tls --ignore-not-found
kubectl create secret generic production-tls --from-file=./tls.key --from-file=./tls.crt.
Ciaran George
  • 571
  • 5
  • 19
2

You can use patch secret:

kubectl patch secret my-secret  --patch="{\"data\": { \"tls.key\": \"$(base64 -w0 ./tls.key)\",\"tls.crt\": \"$(base64 -w0 ./tls.crt)\"  }}" 
Maoz Zadok
  • 4,871
  • 3
  • 33
  • 43
1

I've found a best way to do so:

kubectl create secret generic production-tls --from-file=./tls.key  --from-file=./tls.crt --dry-run=client -o yaml | kubectl apply -f -
ewertonvsilva
  • 1,795
  • 1
  • 5
  • 15