spec:
containers:
…
volumeMounts:
– mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: kube-api-access-tsbwl
readOnly: true
…
volumes:
– name: kube-api-access-tsbwl
projected:
defaultMode: 420
sources:
– serviceAccountToken:
expirationSeconds: 3607
path: token
– configMap:
items:
– key: ca.crt
path: ca.crt
name: kube-root-ca.crt
– downwardAPI:
items:
– fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
path: namespace
Service accounts are also popularly used with Kubernetes RBAC to grant access to Kubernetes API Services to pods. This is done by attaching a ClusterRole
(with necessary permissions) to a service account (by creating a ServiceAccount
object) using a ClusterRoleBinding
. Then we can specify the same service account in the serviceAccountName
of your workload. This would override the default service account that is present per namespace. The default service account token has no permissions to view, list or modify any resources in the cluster.
When Kubernetes attaches the default service account token, it also attaches a configmap of the kube-root-ca.crt
(as seen in the above YAML) that contains the trusted root certificate of the API server. This is used for TLS authentication with the API server when applications communicate with the API server.
Linkerd never needed any of these additional files apart from the token as it never interacts with the Kubernetes API (We will see later how bound service account tokens fixes this).
So how does Linkerd validate that its proxies are who they say they are?
For the proxy to get its certificates, it needs to verify itself with the identity component. This is done by embedding the service account token into the Certify
request that is called every time a new certificate is needed (24hours by default). The identity component validates the token by talking to the TokenReview Kubernetes API and returns a CertifyResponse
with the certificate only after that. The identity component not only verifies that the token is valid, but it also verifies if the token is associated with the same pod that that is requesting the certificate. This can be verified by looking at the Status.User.Username
in the TokenReview
response. Kubernetes API sets the username to the pod name to which that token was attached.
Only the identity component in Linkerd has the necessary API access to verify tokens. Once a token is verified, the identity component issues a certificate for the proxy to use to communicate with other services.
How does Linkerd provide workload identity?
Linkerd takes a beautiful (in my mind) simplifying step here: the service accounts aren’t just used to validate that the proxies are who they say they are, they’re used as the basis of the workload’s identity itself. This gives us a workload identity that is already tied to the capabilities granted to the pod, and means that we can provide mTLS without any additional configuration! This is the secret behind Linkerd’s ability to provide on-by-default mTLS for all meshed pods.
Whenever Linkerd established a mutual TLS connection between two endpoints, the identity exchanged is that of the service account on either side. This identity is even wired into Linkerd’s metrics: whenever a meshed request is received or being sent, the relevant metrics also include the service account with which that peer was associated with.
Here is an example metric from the emojivoto example:
request_total{..., client_id="web.emojivoto.serviceaccount.identity.linkerd.cluster.local", authority="emoji-svc.emojivoto.svc.cluster.local:8080", namespace="emojivoto", pod="emoji-696d9d8f95-5sj4j"} 14532
As you can see the client_id
label in the above metric is the service account that was attached to the client pod from where the request was received.
Authorization Policy
Linkerd’s new authorization policy feature allows users to specify set of clients that can only access a set of resources. This is done by using the same identity to enable users to specify service accounts of the clients that should be allowed to talk to a group of workloads (grouped by the Server
resource) in their ServerAuthorization
resource.
apiVersion: policy.linkerd.io/v1beta1
kind: ServerAuthorization
metadata:
namespace: emojivoto
name: internal-grpc
labels:
app.kubernetes.io/part-of: emojivoto
app.kubernetes.io/version: v11
spec:
server:
selector:
matchLabels:
emojivoto/api: internal-grpc
client:
meshTLS:
serviceAccounts:
– name: web