Authentication and authorization
- Introduction
- Authentication in Kubernetes
- Authentication methods
- Anonymous requests
- Authentication with TLS certificates
- Viewing our admin certificate
- Breaking down the command
- User certificates in practice
- Authentication with tokens
- Service accounts
- Token authentication in practice
- Listing service accounts
- Finding the secret
- Extracting the token
- Using the token
- Results
- Authorization in Kubernetes
- Role-based access control
- From rules to roles to rolebindings
- Cluster-scope permissions
- Pods and service accounts
- In practice
- Creating a service account
- Binding a role to the service account
- Roles vs Cluster Roles
- Users vs Service Accounts
- Testing
- Running
kubectlin the pod - Testing directly with
kubectl - Where do our permissions come from?
- The
system:mastersgroup - Figuring out who can do what
Introduction
And first, a little refresher!
Authentication = verifying the identity of a person
On a UNIX system, we can authenticate with login+password, SSH keys ...
Authorization = listing what they are allowed to do
On a UNIX system, this can include file permissions, sudoer entries ...
Sometimes abbreviated as "authn" and "authz"
In good modular systems, these things are decoupled
(so we can e.g. change a password or SSH key without having to reset access rights)
Authentication in Kubernetes
When the API server receives a request, it tries to authenticate it
(it examines headers, certificates ... anything available)
Many authentication methods are available and can be used simultaneously
(we will see them on the next slide)
It's the job of the authentication method to produce:
- the user name
- the user ID
- a list of groups
The API server doesn't interpret these; it'll be the job of authorizers
Authentication methods
TLS client certificates
(that's what we've been doing with
kubectlso far)Bearer tokens
(a secret token in the HTTP headers of the request)
-
(carrying user and password in a HTTP header)
Authentication proxy
(sitting in front of the API and setting trusted headers)
Anonymous requests
If any authentication method rejects a request, it's denied
(
401 UnauthorizedHTTP code)If a request is neither rejected nor accepted by anyone, it's anonymous
the user name is
system:anonymousthe list of groups is
[system:unauthenticated]
By default, the anonymous user can't do anything
(that's what you get if you just
curlthe Kubernetes API)
Authentication with TLS certificates
This is enabled in most Kubernetes deployments
The user name is derived from the
CNin the client certificatesThe groups are derived from the
Ofields in the client certificateFrom the point of view of the Kubernetes API, users do not exist
(i.e. they are not stored in etcd or anywhere else)
Users can be created (and given membership to groups) independently of the API
The Kubernetes API can be set up to use your custom CA to validate client certs
Viewing our admin certificate
- Let's inspect the certificate we've been using all this time!
Exercise
This command will show the
CNandOfields for our certificate:kubectl config view \ --raw \ -o json \ | jq -r .users[0].user[\"client-certificate-data\"] \ | openssl base64 -d -A \ | openssl x509 -text \ | grep Subject:
Let's break down that command together! 😅
Breaking down the command
kubectl config viewshows the Kubernetes user configuration--rawincludes certificate information (which shows as REDACTED otherwise)-o jsonoutputs the information in JSON format| jq ...extracts the field with the user certificate (in base64)| openssl base64 -d -Adecodes the base64 format (now we have a PEM file)| openssl x509 -textparses the certificate and outputs it as plain text| grep Subject:shows us the line that interests us
→ We are user kubernetes-admin, in group system:masters.
(We will see later how and why this gives us the permissions that we have.)
User certificates in practice
The Kubernetes API server does not support certificate revocation
(see issue #18982)
As a result, we cannot easily suspend a user's access
There are workarounds, but they are very inconvenient:
issue short-lived certificates (e.g. 24 hours) and regenerate them often
re-create the CA and re-issue all certificates in case of compromise
grant permissions to individual users, not groups
(and remove all permissions to a compromised user)
Until this is fixed, we probably want to use other methods
Authentication with tokens
Tokens are passed as HTTP headers:
Authorization: Bearer and-then-here-comes-the-tokenTokens can be validated through a number of different methods:
static tokens hard-coded in a file on the API server
bootstrap tokens (special case to create a cluster or join nodes)
OpenID Connect tokens (to delegate authentication to compatible OAuth2 providers)
service accounts (these deserve more details, coming right up!)
Service accounts
A service account is a user that exists in the Kubernetes API
(it is visible with e.g.
kubectl get serviceaccounts)Service accounts can therefore be created / updated dynamically
(they don't require hand-editing a file and restarting the API server)
A service account is associated with a set of secrets
(the kind that you can view with
kubectl get secrets)Service accounts are generally used to grant permissions to applications, services ...
(as opposed to humans)
Token authentication in practice
We are going to list existing service accounts
Then we will extract the token for a given service account
And we will use that token to authenticate with the API
Listing service accounts
Exercise
- The resource name is
serviceaccountorsain short:kubectl get sa
There should be just one service account in the default namespace: default.
Finding the secret
Exercise
- List the secrets for the
defaultservice account:
It should be namedkubectl get sa default -o yaml SECRET=$(kubectl get sa default -o json | jq -r .secrets[0].name)default-token-XXXXX.
Extracting the token
- The token is stored in the secret, wrapped with base64 encoding
Exercise
View the secret:
kubectl get secret $SECRET -o yamlExtract the token and decode it:
TOKEN=$(kubectl get secret $SECRET -o json \ | jq -r .data.token | openssl base64 -d -A)
Using the token
- Let's send a request to the API, without and with the token
Exercise
Find the ClusterIP for the
kubernetesservice:kubectl get svc kubernetes API=$(kubectl get svc kubernetes -o json | jq -r .spec.clusterIP)Connect without the token:
curl -k https://$APIConnect with the token:
curl -k -H "Authorization: Bearer $TOKEN" https://$API
Results
In both cases, we will get a "Forbidden" error
Without authentication, the user is
system:anonymousWith authentication, it is shown as
system:serviceaccount:default:defaultThe API "sees" us as a different user
But neither user has any right, so we can't do nothin'
Let's change that!
Authorization in Kubernetes
There are multiple ways to grant permissions in Kubernetes, called authorizers:
Node Authorization (used internally by kubelet; we can ignore it)
Attribute-based access control (powerful but complex and static; ignore it too)
Webhook (each API request is submitted to an external service for approval)
Role-based access control (associates permissions to users dynamically)
The one we want is the last one, generally abbreviated as RBAC
Role-based access control
RBAC allows to specify fine-grained permissions
Permissions are expressed as rules
A rule is a combination of:
verbs like create, get, list, update, delete ...
resources (as in "API resource", like pods, nodes, services ...)
resource names (to specify e.g. one specific pod instead of all pods)
in some case, subresources (e.g. logs are subresources of pods)
From rules to roles to rolebindings
A role is an API object containing a list of rules
Example: role "external-load-balancer-configurator" can:
- [list, get] resources [endpoints, services, pods]
- [update] resources [services]
A rolebinding associates a role with a user
Example: rolebinding "external-load-balancer-configurator":
- associates user "external-load-balancer-configurator"
- with role "external-load-balancer-configurator"
Yes, there can be users, roles, and rolebindings with the same name
It's a good idea for 1-1-1 bindings; not so much for 1-N ones
Cluster-scope permissions
API resources Role and RoleBinding are for objects within a namespace
We can also define API resources ClusterRole and ClusterRoleBinding
These are a superset, allowing to:
specify actions on cluster-wide objects (like nodes)
operate across all namespaces
We can create Role and RoleBinding resources within a namespaces
ClusterRole and ClusterRoleBinding resources are global
Pods and service accounts
A pod can be associated to a service account
by default, it is associated to the
defaultservice accountas we've seen earlier, this service account has no permission anyway
The associated token is exposed into the pod's filesystem
(in
/var/run/secrets/kubernetes.io/serviceaccount/token)Standard Kubernetes tooling (like
kubectl) will look for it thereSo Kubernetes tools running in a pod will automatically use the service account
In practice
We are going to create a service account
We will use an existing cluster role (
view)We will bind together this role and this service account
Then we will run a pod using that service account
In this pod, we will install
kubectland check our permissions
Creating a service account
We will call the new service account
viewer(note that nothing prevents us from calling it
view, like the role)
Exercise
Create the new service account:
kubectl create serviceaccount viewerList service accounts now:
kubectl get serviceaccounts
Binding a role to the service account
Binding a role = creating a rolebinding object
We will call that object
viewercanview(but again, we could call it
view)
Exercise
Create the new role binding:
kubectl create rolebinding viewercanview \ --clusterrole=view \ --serviceaccount=default:viewer
It's important to note a couple of details in these flags ...
Roles vs Cluster Roles
We used
--clusterrole=viewWhat would have happened if we had used
--role=view?we would have bound the role
viewfrom the local namespace
(instead of the cluster roleview)the command would have worked fine (no error)
but later, our API requests would have been denied
This is a deliberate design decision
(we can reference roles that don't exist, and create/update them later)
Users vs Service Accounts
We used
--serviceaccount=default:viewerWhat would have happened if we had used
--user=default:viewer?we would have bound the role to a user instead of a service account
again, the command would have worked fine (no error)
... but our API requests would have been denied later
What's about the
default:prefix?that's the namespace of the service account
yes, it could be inferred from context, but ...
kubectlrequires it
Testing
- We will run an
alpinepod and installkubectlthere
Exercise
Run a one-time pod:
kubectl run eyepod --rm -ti --restart=Never \ --serviceaccount=viewer \ --image alpineInstall
curl, then use it to installkubectl:apk add --no-cache curl URLBASE=https://storage.googleapis.com/kubernetes-release/release KUBEVER=$(curl -s $URLBASE/stable.txt) curl -LO $URLBASE/$KUBEVER/bin/linux/amd64/kubectl chmod +x kubectl
Running kubectl in the pod
- We'll try to use our
viewpermissions, then to create an object
Exercise
Check that we can, indeed, view things:
./kubectl get allBut that we can't create things:
./kubectl create deployment testrbac --image=nginxExit the container with
exitor^D
Testing directly with kubectl
We can also check for permission with
kubectl auth can-i:kubectl auth can-i list nodes kubectl auth can-i create pods kubectl auth can-i get pod/name-of-pod kubectl auth can-i get /url-fragment-of-api-request/ kubectl auth can-i '*' servicesAnd we can check permissions on behalf of other users:
kubectl auth can-i list nodes \ --as some-user kubectl auth can-i list nodes \ --as system:serviceaccount:<namespace>:<name-of-service-account>
Where do our permissions come from?
When interacting with the Kubernetes API, we are using a client certificate
We saw previously that this client certificate contained:
CN=kubernetes-adminandO=system:mastersLet's look for these in existing ClusterRoleBindings:
kubectl get clusterrolebindings -o yaml | grep -e kubernetes-admin -e system:masters(
system:mastersshould show up, but notkubernetes-admin.)Where does this match come from?
The system:masters group
If we eyeball the output of
kubectl get clusterrolebindings -o yaml, we'll find out!It is in the
cluster-adminbinding:kubectl describe clusterrolebinding cluster-adminThis binding associates
system:mastersto the cluster rolecluster-adminAnd the
cluster-adminis, basically,root:kubectl describe clusterrole cluster-admin
Figuring out who can do what
For auditing purposes, sometimes we want to know who can perform an action
Here is a proof-of-concept tool by Aqua Security, doing exactly that:
This is one way to install it:
docker run --rm -v /usr/local/bin:/go/bin golang \ go get -v github.com/aquasecurity/kubectl-who-canThis is one way to use it:
kubectl-who-can create pods