I am using KubeSpray to provision a two node cluster on AWS. By default, the --kubelet-certificate-authority parameter is not used. However, I would like to set it.
I do not know the correct setting for --kubelet-certificate-authority. When I set it to /etc/kubernetes/pki/ca.crt I see messages like the following in my logs:
TLS handshake error from 10.245.207.223:59278: remote error: tls: unknown certificate authority
In order to create an isolated environment for testing, I SSH'ed to my controller node to run this command which runs apiserver on the non-standard port of 4667. I copied these values directly from /etc/kubernetes/manifests/kube-apiserver.yaml. You'll need to adjust the value to match your own cluster. I purposely am running the container in interactive mode so that I can see the log messages.
sudo docker run \
--name api-server-playground \
-it \
--rm \
--network host \
--volume /etc/kubernetes:/etc/kubernetes:ro \
--volume /etc/pki:/etc/pki:ro \
--volume /etc/ssl:/etc/ssl/:ro \
k8s.gcr.io/kube-apiserver:v1.18.9 \
kube-apiserver \
--advertise-address=10.245.207.223 \
--allow-privileged=true \
--anonymous-auth=True \
--apiserver-count=1 \
--authorization-mode=Node,RBAC \
--bind-address=0.0.0.0 \
--client-ca-file=/etc/kubernetes/ssl/ca.crt \
--cloud-config=/etc/kubernetes/cloud_config \
--cloud-provider=aws \
--enable-admission-plugins=NodeRestriction \
--enable-aggregator-routing=False \
--enable-bootstrap-token-auth=true \
--endpoint-reconciler-type=lease \
--etcd-cafile=/etc/ssl/etcd/ssl/ca.pem \
--etcd-certfile=/etc/ssl/etcd/ssl/node-ip-10-245-207-223.ec2.internal.pem \
--etcd-keyfile=/etc/ssl/etcd/ssl/node-ip-10-245-207-223.ec2.internal-key.pem \
--etcd-servers=https://10.245.207.119:2379 \
--event-ttl=1h0m0s \
--insecure-port=0 \
--kubelet-client-certificate=/etc/kubernetes/ssl/apiserver-kubelet-client.crt \
--kubelet-client-key=/etc/kubernetes/ssl/apiserver-kubelet-client.key \
--kubelet-preferred-address-types=InternalDNS,InternalIP,Hostname,ExternalDNS,ExternalIP \
--profiling=False \
--proxy-client-cert-file=/etc/kubernetes/ssl/front-proxy-client.crt \
--proxy-client-key-file=/etc/kubernetes/ssl/front-proxy-client.key \
--request-timeout=1m0s \
--requestheader-allowed-names=front-proxy-client \
--requestheader-client-ca-file=/etc/kubernetes/ssl/front-proxy-ca.crt \
--requestheader-extra-headers-prefix=X-Remote-Extra- \
--requestheader-group-headers=X-Remote-Group \
--requestheader-username-headers=X-Remote-User \
--secure-port=6447 \
--service-account-key-file=/etc/kubernetes/ssl/sa.pub \
--service-cluster-ip-range=10.233.0.0/18 \
--service-node-port-range=30000-32767 \
--storage-backend=etcd3 \
--tls-cert-file=/etc/kubernetes/ssl/apiserver.crt \
--tls-private-key-file=/etc/kubernetes/ssl/apiserver.key
Now it's possible to SSH into the controller again and use curl to interact with the custom apiserver container.
- Set the APISERVER variable to point to the controller node. I'm using the value of the
advertise-addressparameter above.
APISERVER=https://10.245.207.223:6447
- Get a token.
TOKEN=$(kubectl get secrets -o jsonpath="{.items[?(@.metadata.annotations['kubernetes\.io/service-account\.name']=='default')].data.token}"|base64 --decode); echo "TOKEN=$TOKEN"
- Make an api request. This request will fail because "Peer's Certificate issuer is not recognized.".
curl --header "Authorization: Bearer $TOKEN" -X GET $APISERVER/api
An error message will appear in the docker log. It will look like this:
I0921 14:39:07.662368 1 log.go:172] http: TLS handshake error
from 10.245.207.223:59278: remote error: tls: unknown certificate authority
- Now use the
-kcurl parameter to bypass the recognition issue.
curl -k --header "Authorization: Bearer $TOKEN" -X GET $APISERVER/api
{
"kind": "APIVersions",
"versions": [
"v1"
],
"serverAddressByClientCIDRs": [
{
"clientCIDR": "0.0.0.0/0",
"serverAddress": "10.245.207.223:6447"
}
]
}
You'll see the request works correctly. However, I don't want to use the -k parameter. So I tried to use the certificate authority from the apiserver.
- Get the certificate authority from the apiserver.
echo | \
openssl s_client -connect $APISERVER 2>/dev/null | \
openssl x509 -text | \
sed -n "/BEGIN CERTIFICATE/,/END CERTIFICATE/p" \
> apiserver.ca.crt
- Use the certificate authority file for the api request.
curl --cacert apiserver.ca.crt --header "Authorization: Bearer $TOKEN" -X GET $APISERVER/api
UPDATE
Following a thought prompted by Wiktor's response, I added /etc/kubernetes/ssl/ca.crt as the certificate authority. And used that file in my curl command.
curl --cacert /etc/kubernetes/ssl/ca.crt --header "Authorization: Bearer $TOKEN" -X GET $APISERVER/api
This worked.
In order to make the
--kubelet-certificate-authorityflag work you first need to make sure you got Kubelet authentication and Kubelet authorization enabled. After that you can follow the Kubernetes documentation and setup the TLS connection between the apiserver and kubelet. And finally, you can edit the API server pod specification file/etc/kubernetes/manifests/kube-apiserver.yamlon the master node and set the--kubelet-certificate-authorityparameter to the path to the cert file for the certificate authority.So, to sum up the steps to do are:
start the kubelet with the
--anonymous-auth=falseflagstart the kubelet with the
--client-ca-fileflag, providing a CA bundle to verify client certificates withstart the apiserver with
--kubelet-client-certificateand--kubelet-client-keyflagsensure the
authentication.k8s.io/v1beta1API group is enabled in the API serverstart the kubelet with the
--authentication-token-webhookand--kubeconfig flagsthe kubelet calls the
TokenReviewAPI on the configured API server to determine user information from bearer tokensensure the
authorization.k8s.io/v1beta1API group is enabled in the API serverstart the kubelet with the
--authorization-mode=Webhookand the--kubeconfigflagsthe kubelet calls the
SubjectAccessReviewAPI on the configured API server to determine whether each request is authorized--kubelet-certificate-authorityflag to provide the apiserver with a root certificate bundle to use to verify the kubelet's serving certificate.More details can be found in the linked documentation.