Kubernetes Service Accounts and Secrets - Mounting Secrets as Volumes
- 3 minutes read - 537 wordsA Service Account provides an identity for processes that run in a pod. When processes inside a pod contact the API server, they are authenticated as a particular Service Account.
Create a Service Account using the YAML below.
apiVersion: v1
kind: ServiceAccount
metadata:
name: sa-app-name
namespace: namespace-name
Once the Service Account is created, you can reference it in your pod spec:
apiVersion: v1
kind: Pod
metadata:
name: app-name
spec:
serviceAccountName: sa-app-name
Now on to Kubernetes Secrets. A Secret lets you store and manage sensitive information. Here we’ll create two secrets for our tests: mysecret1 and mysecret2. Note the --- separator - without it, the second document silently overwrites the first.
apiVersion: v1
kind: Secret
metadata:
name: mysecret1
type: Opaque
data:
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm
---
apiVersion: v1
kind: Secret
metadata:
name: mysecret2
type: Opaque
data:
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm
Mount both secrets as volumes in the pod spec, and use the Service Account.
apiVersion: v1
kind: Pod
metadata:
name: app-name
spec:
serviceAccountName: sa-app-name
containers:
- name: mypod
image: redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
- name: boo
mountPath: "/etc/boo"
readOnly: true
volumes:
- name: foo
secret:
secretName: mysecret1
- name: boo
secret:
secretName: mysecret2
Create a Role that grants access to mysecret1 only.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: secret-role
namespace: namespace-name
rules:
- apiGroups:
- ""
resources:
- secrets
resourceNames:
- mysecret1
verbs:
- get
Create a RoleBinding that binds the Role to the Service Account.
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: myrolebinding
namespace: namespace-name
roleRef:
kind: Role
name: secret-role
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: sa-app-name
namespace: namespace-name
OK, the hard work is done. What do you expect will happen? Will the pod be able to mount mysecret2 as a volume? What does the RoleBinding actually do? Let’s find out.
On creating the pod, we find that both mysecret1 and mysecret2 are mounted as volumes. The Service Account RBAC has no impact on the volume mounts - both secrets are accessible to applications inside the pod.
However, querying the Kubernetes API from within the pod gives different results.
$ curl -sSk -H "Authorization: Bearer $KUBE_TOKEN" \
https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT/api/v1/namespaces/namespace-name/secrets/mysecret1
{
"kind": "Secret",
"apiVersion": "v1",
"metadata": {
"name": "mysecret1",
"namespace": "namespace-name"
}
...
}
# SUCCESS
$ curl -sSk -H "Authorization: Bearer $KUBE_TOKEN" \
https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT/api/v1/namespaces/namespace-name/secrets/mysecret2
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {},
"status": "Failure",
"message": "secrets \"mysecret2\" is forbidden: User \"user-name\" cannot get resource \"secrets\" in API group \"\" in the namespace \"namespace-name\"",
"reason": "Forbidden",
"details": {
"name": "mysecret2",
"kind": "secrets"
},
"code": 403
}
# FAIL
So mysecret1 is accessible via the Kubernetes API, but mysecret2 is forbidden - exactly as the RBAC Role dictates. Yet both secrets can still be mounted as volumes and are accessible within the pod. This is because the volume is mounted by the kubelet, which is not bound by the pod’s Service Account RBAC.
The takeaway: RBAC on a Service Account controls what the pod’s identity can fetch from the API, not what the kubelet can mount for it. If you need to stop a pod mounting a particular secret, RBAC alone won’t do it. This is the question I asked on Stack Overflow about denying specific secrets from being mounted as volumes in a given pod.
Share on: