Edit kubernetes secrets and kubectl plugins

December 05, 2023

In Kubernetes we can use the Secret object to store any sensitive information to be used within the Pod app without the need of being hard-coded inside the Pod spec or inside the container image.

Secrets values are stored by default as base64 encoded strings, which means we need to encode them before being saved them(and decode if we want to see its values) which can make the whole process a little bit cumbersome but easily solvable with some scripting.

In the typical workflow, we would create an Opaque Secret like this:

➜ kubectl create secret generic my-app
secret/my-app created

and in order to store the value BAR in the FOO key, we have to encode the string BAR into base64 and then add the data content in the Secret definition as follows:

echo -n "BAR" | base64 # note the -n flag to remove the newline in the generated output
QkFS

→ kubectl edit secret my-app
data:
  FOO: QkFS
apiVersion: v1
kind: Secret
metadata:
  creationTimestamp: "2021-02-18T20:15:41Z"
  name: my-app
  namespace: default
  resourceVersion: "197083"
  uid: 1560dba2-f0e5-411e-bcc6-17fb47f20955
type: Opaque

After saving the file, the Secret will be sent to the k8s cluster and can be retrieved with the command

➜ kubectl get secret my-app -o json
{
    "apiVersion": "v1",
    "data": {
        "FOO": "QkFS"
    },
    "kind": "Secret",
...

while this is a relative straightforward process, it can be a little bit tedious and luckily there are different ways to make it easier.

For example, we can run everything in a single command without requiring any manual edit with something like

echo -n "FOOBAR" \
  | base64 \
  | read output; kubectl patch secret my-app -p="{\"data\":{\"FOO\": \"$output\"}}"

This command could be added in a function/script to be reused when needed but we can do a little bit better: it turns out it's quite easy to create our custom kubectl plugins so they are better integrated in our workflow.

Any executable available in the $PATH env var with the kubectl-whatever file name format, will expose an option called whatever to the kubectl command. That means that if for example, we have a program called kubectl-util in the path written in our language of choice, we will able to call it by executing the command kubectl util (and obviously pass any parameter we might need).

I wrote an example in ruby that allows to set multiple values for the same Secret and restart the Deployment so they are available to the running pods(it assumes both secret and deployment have the same name). It also allows to choose the namespace as the target environment. Your architecture might differ and you might have a different cluster per environment, so it could be easily adapted to change between clusters depending on the target environment option(eg. you could use kubectx which is really useful): https://github.com/dagi3d/kubectl-util

Setting a config variable would be now as easy as running something like

➜ kubectl util config:set FOO=BAR -a my-app
secret/my-app patched
deployment.apps/my-app restarted

and read them with

➜ kubectl util config:get -a my-app

Bear in mind, that it is also recommended to implement other security practices while managing sensitive information, specially considering that k8s Secrets are stored in plain text by default, but this would be out of the scope of this post.