In this post, I will talk about admission controller, one of the key component of API server(document reference). In the past few posts, we deployed PKI to secure the communication between each component. It’s time to secure/validate the requests itself. In previous post, I illustrated how API server deals the request, and it can be summarized as follows:
- Authentication … Is the requester a valid account?
- Authorization … Is the requester allowed to do what it request?
- Admission Control … Allow/Reject/Modify the original request based on various criteria.
You may encounter problems like pods not launching with kubectl run command with the recent kubernetes packages. It might be the case related to this admission controller, because kubernetes apiserver launches admission controller enabled by default. In order to successfully launch pods, service account needs to be correctly setup.
Service Account Setup
At this step, we do have service account, which is created by default(and named default) when we launch the cluster. But it doesn’t have any token which is used for authorization.
[ Controller-1 ]
root@controller-1:/var/lib/kubernetes/cert# kubectl get sa NAME SECRETS AGE default 0 22d root@controller-1:/var/lib/kubernetes/cert# kubectl describe sa/default Name: default Namespace: default Labels: <none> Annotations: <none> Image pull secrets: <none> Mountable secrets: <none> Tokens: <none> Events: <none>
1. Create key files for Service Account setup
In order for admission controller to successfully serve ServiceAccount plugin, both Token controller(which resides in controller-manager) and API server needs to load the key files as per the official document.
First we create key files:
[ controller-1 ]
root@controller-1:/var/lib/kubernetes/cert# cat << EOF > sa-csr.json > { > "CN": "admin", > "key": { > "algo": "rsa", > "size": 2048 > }, > "names": [ > { > "C": "UK", > "L": "London", > "O": "system:masters", > "OU": "k8s Demo" > } > ] > } > EOF root@controller-1:/var/lib/kubernetes/cert# root@controller-1:/var/lib/kubernetes/cert# cfssl gencert \ > -ca=ca.pem \ > -ca-key=ca-key.pem \ > -config=ca-config.json \ > -profile=kubernetes \ > sa-csr.json | cfssljson -bare sa 2018/10/18 22:31:07 [INFO] generate received request 2018/10/18 22:31:07 [INFO] received CSR 2018/10/18 22:31:07 [INFO] generating key: rsa-2048 2018/10/18 22:31:07 [INFO] encoded CSR 2018/10/18 22:31:07 [INFO] signed certificate with serial number
2. Modify API server system service file
Modify API server service systemd file so that it loads the key file for service account setup to be loaded on launch.
[ Controller-1 ]
root@controller-1:/var/lib/kubernetes/cert# cat /etc/systemd/system/kube-apiserver.service [Unit] Description=Kubernetes API Server Documentation=https://github.com/kubernetes/kubernetes [Service] ExecStart=/usr/local/bin/kube-apiserver --etcd-servers=http://127.0.0.1:2379 --service-cluster-ip-range=10.32.0.0/24 --insecure-bind-address=0.0.0.0 --client-ca-file=/var/lib/kubernetes/cert/ca.pem --kubelet-certificate-authority=/var/lib/kubernetes/cert/ca.pem --kubelet-client-certificate=/var/lib/kubernetes/cert/apiserver.pem --kubelet-client-key=/var/lib/kubernetes/cert/apiserver-key.pem --tls-cert-file=/var/lib/kubernetes/cert/apiserver.pem --tls-private-key-file=/var/lib/kubernetes/cert/apiserver-key.pem --service-account-key-file=/var/lib/kubernetes/cert/sa.pem Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target root@controller-1:/var/lib/kubernetes/cert# systemctl daemon-reload root@controller-1:/var/lib/kubernetes/cert# systemctl restart kube-apiserver root@controller-1:/var/lib/kubernetes/cert# systemctl status kube-apiserver ● kube-apiserver.service - Kubernetes API Server Loaded: loaded (/etc/systemd/system/kube-apiserver.service; enabled; vendor preset: enabled) Active: active (running) since Thu 2018-10-18 22:46:10 UTC; 6s ago Docs: https://github.com/kubernetes/kubernetes Main PID: 3067 (kube-apiserver) Tasks: 6 Memory: 283.1M CPU: 6.147s CGroup: /system.slice/kube-apiserver.service └─3067 /usr/local/bin/kube-apiserver --etcd-servers=http://127.0.0.1:2379 --service-cluster-ip-range=10.32.0.0/24 --insecure-bind-address=0.0.0.0 --client-ca-file=/var/lib/ku Oct 18 22:46:11 controller-1 kube-apiserver[3067]: W1018 22:46:11.569043 3067 genericapiserver.go:342] Skipping API batch/v2alpha1 because it has no resources. Oct 18 22:46:11 controller-1 kube-apiserver[3067]: W1018 22:46:11.581813 3067 genericapiserver.go:342] Skipping API rbac.authorization.k8s.io/v1alpha1 because it has no resources. Oct 18 22:46:11 controller-1 kube-apiserver[3067]: W1018 22:46:11.585110 3067 genericapiserver.go:342] Skipping API storage.k8s.io/v1alpha1 because it has no resources. Oct 18 22:46:11 controller-1 kube-apiserver[3067]: W1018 22:46:11.596088 3067 genericapiserver.go:342] Skipping API admissionregistration.k8s.io/v1alpha1 because it has no resources. Oct 18 22:46:11 controller-1 kube-apiserver[3067]: [restful] 2018/10/18 22:46:11 log.go:33: [restful/swagger] listing is available at https://10.240.0.11:6443/swaggerapi Oct 18 22:46:11 controller-1 kube-apiserver[3067]: [restful] 2018/10/18 22:46:11 log.go:33: [restful/swagger] https://10.240.0.11:6443/swaggerui/ is mapped to folder /swagger-ui/ Oct 18 22:46:13 controller-1 kube-apiserver[3067]: [restful] 2018/10/18 22:46:13 log.go:33: [restful/swagger] listing is available at https://10.240.0.11:6443/swaggerapi Oct 18 22:46:13 controller-1 kube-apiserver[3067]: [restful] 2018/10/18 22:46:13 log.go:33: [restful/swagger] https://10.240.0.11:6443/swaggerui/ is mapped to folder /swagger-ui/ Oct 18 22:46:13 controller-1 kube-apiserver[3067]: W1018 22:46:13.823297 3067 admission.go:68] PersistentVolumeLabel admission controller is deprecated. Please remove this controller Oct 18 22:46:13 controller-1 kube-apiserver[3067]: I1018 22:46:13.825063 3067 plugins.go:149] Loaded 9 admission controller(s) successfully in the following order: NamespaceLifecycle root@controller-1:/var/lib/kubernetes/cert#
3. Modify Controller-Manager system service file
Modify Controller Manager systemd file so that it loads the service account private key file on launch.
root@controller-1:/var/lib/kubernetes/cert# cat /etc/systemd/system/kube-controller-manager.service [Unit] Description=Kubernetes Controller Manager Server Documentation=https://github.com/kubernetes/kubernetes [Service] ExecStart=/usr/local/bin/kube-controller-manager --master=http://127.0.0.1:8080 --service-account-private-key-file=/var/lib/kubernetes/cert/sa-key.pem Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target root@controller-1:/var/lib/kubernetes/cert# systemctl daemon-reload root@controller-1:/var/lib/kubernetes/cert# systemctl restart kube-controller-manager root@controller-1:/var/lib/kubernetes/cert# systemctl status kube-controller-manager ● kube-controller-manager.service - Kubernetes Controller Manager Server Loaded: loaded (/etc/systemd/system/kube-controller-manager.service; enabled; vendor preset: enabled) Active: active (running) since Thu 2018-10-18 22:43:39 UTC; 7s ago Docs: https://github.com/kubernetes/kubernetes Main PID: 2994 (kube-controller) Tasks: 5 Memory: 66.1M CPU: 102ms CGroup: /system.slice/kube-controller-manager.service └─2994 /usr/local/bin/kube-controller-manager --master=http://127.0.0.1:8080 --service-account-private-key-file=/var/lib/kubernetes/cert/sa-key.pem Oct 18 22:43:39 controller-1 systemd[1]: Stopped Kubernetes Controller Manager Server. Oct 18 22:43:39 controller-1 systemd[1]: Started Kubernetes Controller Manager Server. Oct 18 22:43:39 controller-1 kube-controller-manager[2994]: I1018 22:43:39.311807 2994 controllermanager.go:116] Version: v1.10.8 Oct 18 22:43:39 controller-1 kube-controller-manager[2994]: W1018 22:43:39.313264 2994 authentication.go:55] Authentication is disabled Oct 18 22:43:39 controller-1 kube-controller-manager[2994]: I1018 22:43:39.313528 2994 insecure_serving.go:44] Serving insecurely on [::]:10252 Oct 18 22:43:39 controller-1 kube-controller-manager[2994]: I1018 22:43:39.313925 2994 leaderelection.go:175] attempting to acquire leader lease kube-system/kube-controller-manager. root@controller-1:/var/lib/kubernetes/cert#
4. Confirmation
Now we can see if it works well. First check if token is generated for default user.
root@controller-1:/var/lib/kubernetes/cert# kubectl get sa NAME SECRETS AGE default 1 22d root@controller-1:/var/lib/kubernetes/cert# kubectl describe sa/default Name: default Namespace: default Labels: <none> Annotations: <none> Image pull secrets: <none> Mountable secrets: default-token-wcvlj Tokens: default-token-wcvlj Events: <none>
Looks good. And let’s see if we can launch pod and how the token is used.
# create deployment root@controller-1:/var/lib/kubernetes/cert# kubectl run nginx --image=nginx deployment.apps "nginx" created root@controller-1:/var/lib/kubernetes/cert# kubectl get deploy NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE nginx 1 1 1 1 7s root@controller-1:/var/lib/kubernetes/cert# kubectl get pods NAME READY STATUS RESTARTS AGE nginx-65899c769f-pgr7r 1/1 Running 0 18s root@controller-1:/var/lib/kubernetes/cert# kubectl describe pods Name: nginx-65899c769f-pgr7r Namespace: default Node: worker-2/10.240.0.22 Start Time: Thu, 18 Oct 2018 22:46:44 +0000 Labels: pod-template-hash=2145573259 run=nginx Annotations: <none> Status: Running IP: 10.200.2.3 Controlled By: ReplicaSet/nginx-65899c769f Containers: nginx: Container ID: docker://7d624d1fd19762c02538ad00e712822c39d8dfa6b04ed643ea5f05504e37a200 Image: nginx Image ID: docker-pullable://nginx@sha256:5704bcdeec8715eb71c95a425f4c4a14264d8c6f92a0b105c23933a2eb503b63 Port: <none> Host Port: <none> State: Running Started: Thu, 18 Oct 2018 22:46:46 +0000 Ready: True Restart Count: 0 Environment: <none> Mounts: /var/run/secrets/kubernetes.io/serviceaccount from default-token-wcvlj (ro) Conditions: Type Status Initialized True Ready True ContainersReady True PodScheduled True Volumes: default-token-wcvlj: Type: Secret (a volume populated by a Secret) SecretName: default-token-wcvlj Optional: false QoS Class: BestEffort Node-Selectors: <none> Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s node.kubernetes.io/unreachable:NoExecute for 300s Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 31s default-scheduler Successfully assigned nginx-65899c769f-pgr7r to worker-2 Normal Pulling 30s kubelet, worker-2 pulling image "nginx" Normal Pulled 29s kubelet, worker-2 Successfully pulled image "nginx" Normal Created 29s kubelet, worker-2 Created container Normal Started 29s kubelet, worker-2 Started container
It looks ok, serviceaccount token is mounted on the container. And the container uses this credential if it needs to communicate with Kubernetes service.
Let’s see the contents of this directory.
root@controller-1:/var/lib/kubernetes/cert# kubectl exec -it nginx-65899c769f-pgr7r -- /bin/bash root@nginx-65899c769f-pgr7r:/# ls -l /var/run/secrets/kubernetes.io/serviceaccount total 0 lrwxrwxrwx 1 root root 16 Oct 18 22:46 namespace -> ..data/namespace lrwxrwxrwx 1 root root 12 Oct 18 22:46 token -> ..data/token # we can see the namespace root@nginx-65899c769f-pgr7r:/# cat /var/run/secrets/kubernetes.io/serviceaccount/namespace default # and this is the actual token to be used root@nginx-65899c769f-pgr7r:/# cat /var/run/secrets/kubernetes.io/serviceaccount/token eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9...... root@nginx-65899c769f-pgr7r:/# exit