kubectl apply with stringData in secret does not remove fields - kubernetes

(using kubernetes v1.15.7 in minikube and matching client version and minikube 1.9.0)
If I kubectl apply a secret like this:
apiVersion: v1
data:
MY_KEY: dmFsdWUK
MY_SECRET: c3VwZXJzZWNyZXQK
kind: Secret
metadata:
name: my-secret
type: Opaque
then subsequently kubectl apply a secret removing the MY_SECRET field, like this:
apiVersion: v1
data:
MY_KEY: dmFsdWUK
kind: Secret
metadata:
name: my-secret
type: Opaque
The data field in the result is what I expect when I kubectl get the secret:
data:
MY_KEY: dmFsdWUK
However, if I do the same thing using stringData instead for the first kubectl apply, it does not remove the missing key on the second one:
First kubectl apply:
apiVersion: v1
stringData:
MY_KEY: value
MY_SECRET: supersecret
kind: Secret
metadata:
name: my-secret
type: Opaque
Second kubectl apply (stays the same, except replacing MY_KEY's value with b2hubyEK to show the configuration DID change)
apiVersion: v1
data:
MY_KEY: b2hubyEK
kind: Secret
metadata:
name: my-secret
type: Opaque
kubectl get result after applying the second case:
data:
MY_KEY: b2hubyEK
MY_SECRET: c3VwZXJzZWNyZXQ=
The field also does not get removed if the second case uses stringData instead. So it seems that once stringData is used once, it's impossible to remove a field without deleting the secret. Is this a bug? Or should I be doing something differently when using stringData?

kubectl apply need to merge / patch the changes here. How this works is described in How apply calculates differences and merges changes
I would recommend to use kustomize with kubectl apply -k and use the secretGenerator to create a unique secret name, for every change. Then you are practicing Immutable Infrastructure and does not get this kind of problems.
A brand new tool for config manangement is kpt, and kpt live apply may also be an interesting solution for this.

The problem is that stringData is a write only field. It doesn’t have convergent behavior so it breaks the merge patch generator system. Most high level tools fix this by converting to normal data before dealing with the patch.

Related

How to affect the order in which imagePullSecrets are used?

If I add (by oc/kubectl patch like this) a custom image pull secret to the default serviceaccount (in k8s or OKD), and given that I cannot effectively delete the default image pull secret (it gets automatically regenerated), how can I determine and control the order in which imagePullSecrets are used (to use custom one first, before default is used)?
Note that after such patching of the default serviceaccount with the custom Secret (here named readonly-docker-hub-cred-for-mirekphd) this secret gets always inserted at the top (above default-dockercfg-* ones that get auto-regenerated each time I delete them), but does this top position give it also priority when pulling images (over the default one with much worse pull rate limits)?:
$ oc get sa default -o yaml
apiVersion: v1
imagePullSecrets:
- name: readonly-docker-hub-cred-for-mirekphd
- name: default-dockercfg-?????
kind: ServiceAccount
metadata:
creationTimestamp: 20??-08-09T06:57:32Z
name: default
namespace: ??
resourceVersion: "370297143"
selfLink: /api/v1/namespaces/??/serviceaccounts/default
uid: 7d5ac6cf-9ba1-11e8-bfd8-0050569f????
secrets:
- name: default-token-?????
- name: default-dockercfg-?????

Removed k8s secret's data persists after updates

I have a k8s secret yaml definition with some data items already applied in the cluster. After removing some data items from the yaml file, and updating the secret with kubectl apply, those removed data items still persists in the secret object existing in the k8s cluster, not being able to remove them without deleting and recreating the secret from scratch. However, this is not the usual behavior and only happens on rare occasions. Any idea why this is happening and how can I fix it without deleting the whole secret?
Example:
$ cat <<EOF|kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
name: db-credentials-secret
namespace: default
type: Opaque
stringData:
user: foo
password: bar
EOF
The secret is created with data items user and password.
$ cat <<EOF|kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
name: db-credentials-secret
namespace: default
type: Opaque
stringData:
password: bar
EOF
After removing user from the secret definition, the secret is updated with kubectl apply but the user data item still remains in the secret.

How to deploy a secret resource?

I am new to Kubernetes, and trying to wrap my head around how to set secrets. I will try to illustrate with an example. Let's say I have a secret resource that looks like:
apiVersion: v1
kind: Secret
metadata:
name: big-secrets
type: Opaque
data:
secret-password: "secretMeow"
secret-key: "angryWoof"
database-password: "happyWhale"
How do I set up (not sure if set up is the right word here) these secrets using kubectl, and how do I retrieve them? I have tried reading through the following documentation but I am still not sure how to set up from a yaml file as mentioned above.
Using stringData it is possible to create a secret using plain text values:
apiVersion: v1
kind: Secret
metadata:
name: big-secrets
type: Opaque
stringData:
secret-password: "secretMeow"
secret-key: "angryWoof"
database-password: "happyWhale"
However it's worth noting it's provided more for convenience and the docs say:
It is provided as a write-only convenience method. All keys and values are merged into the data field on write, overwriting any existing values. It is never output when reading from the API.
For the question of how to read the secret, if you wanted it programatically, you could use a tool like jq to parse the Kubernetes output and base64 decode:
kubectl get secret big-secrets -o json | jq -r '.data["secret-password"] | #base64d'
This would get the created secret as JSON (-o yaml is also an option), read the data field for a given secret (secret-password in this case) and then base64 decode it.
Using yaml file you cannot create secret without encoded string value. Secret data value must be in base64 encoded in yaml file. If you want to create a secret from yaml file then you need to decode data value like below.
apiVersion: v1
kind: Secret
metadata:
name: big-secrets
type: Opaque
data:
secret-password: c2VjcmV0TWVvdwo=
secret-key: YW5ncnlXb29mCg==
database-password: aGFwcHlXaGFsZQo=
Or
You can use kubectl imperative command to create a secret from literal values like below.
kubectl create secret generic big-secrets --from-literal=secret-password="secretMeow" --from-literal=secret-key="angryWoof"

Use Kubernetes Secret as Environment Variable when secret is create from a file

I have created a kubernetes secret from a file (secret.txt):
k1=v1
k2=v2
k3=v3
It looks like this secret-:
apiVersion: v1
metadata:
name: secret007
data:
secret.txt: bWFza1NhbHQ9InRlc3RzYWx0IgpzM1
I am using it as environment variable in the pod like this:
- name: KEY1
valueFrom:
secretKeyRef:
key: k1
name: secret007
optional: false
Problem
Problem since the data has a value as a single base64 value. I am not able to refer it in the pod and getting this error.
Warning Failed 6s (x2 over 6s) kubelet, Error: couldn't find key k1 in Secret kube-system/secret007
Please suggest how to do this without changing the secret format i.e Secret would be a single key value of filename and all secret.txt values as a single base64 value. Is it possible?
You can create secret entries from environment file
kubectl create secret generic test --from-env-file=secret.txt
and the output will have distinct values in your secret
apiVersion: v1
data:
k1: djE=
k2: djI=
k3: djM=
kind: Secret
metadata:
creationTimestamp: null
name: test
This is a community wiki answer posted for better visibility. Feel free to expand it.
As already mentioned by #DavidMaze, Kubernetes will not to look "inside" a Secret or a ConfigMap values:
You can refer to the entire Secret value as you've shown in the
question, but there's no way to tell it (a) that the value is actually
newline-separated key=value pairs (as opposed to TOML, YAML, JSON,
XML, ...) and (b) that you want to pick some specific value out of
there.

What does key mean when creating a kubernetes configmap from a file

I see here a syntax like this:
kubectl create cm configmap4 --from-file=special=config4.txt
I did not find a description of what repetition of = and the special means here.
Kubernetes documentation here only denotes one time usage of = after --from-file while creating configmaps in kubectl.
It appears from generating the YAML that this middle key mean all the keys that are being loaded from the file to be nested inside the mentioned key (special keyword in the question example).
It appears like this:
apiVersion: v1
data:
special: |
var3=val3
var4=val4
kind: ConfigMap
metadata:
creationTimestamp: "2019-06-01T08:20:15Z"
name: configmap4
namespace: default
resourceVersion: "123320"
selfLink: /api/v1/namespaces/default/configmaps/configmap4
uid: 1582b155-8446-11e9-87b7-0800277f619d
kubectl create configmap my-config --from-file=path/to/bar
When creating a configmap based on a file, the key will default to the basename of the file, and the value will default to the file content. If the basename is an invalid key, you may specify an alternate key.
Create a new configmap named my-config with specified keys instead of file basenames on disk
kubectl create configmap my-config --from-file=key1=/path/to/bar/file1.txt --from-file=key2=/path/to/bar/file2.txt