Kubernetes the hard way on bare metal/VMs — Install CloudFlare’s PKI Toolkit and generate TLS certs

Part of the Kubernetes the hard way on bare metal/VM

Drew Viles
5 min readDec 14, 2018
Cloudflare Logo

Introduction

This guide is part of the Kubernetes the hard way on bare metal/VMs series. On its own this may be useful to you however since it’s tailored for the series, it may not be completely suited to your needs.

You’re using CloudFlare’s PKI toolkit for the generation of the certificate authority and for generating the TLS certs that the cluster services and users will use to interact with the cluster. As always other options such as openSSL can be used.

Install cfssl

wget -q --show-progress --https-only --timestamping https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64chmod +x cfssl_linux-amd64 cfssljson_linux-amd64
sudo mv cfssl_linux-amd64 /usr/local/bin/cfssl
sudo mv cfssljson_linux-amd64 /usr/local/bin/cfssljson

Provisioning CA

Create a few directories for the TLS certs to live in.

mkdir -p pki/{admin,api,ca,clients,controller,proxy,scheduler,service-account}

To generate the TLS certificates, you’ll need some configurations files; you’ll create each one as you generate the TLS cert.

Don’t just copy and paste the small section below as you will need to change the content of the environment variables below. They’ll be used in the configs to generate the TLS certs.

TLS_C="GB"
TLS_L="TOWN"
TLS_OU="DeeToTheVee Kubernetes"
TLS_ST="CITY"

Generate the CA (Certificate Authority) config files & certificates

cat > pki/ca/ca-config.json <<EOF
{
"signing": {
"default": {
"expiry": "8760h"
},
"profiles": {
"kubernetes": {
"usages": ["signing", "key encipherment", "server auth", "client auth"],
"expiry": "8760h"
}
}
}
}
EOF
cat > pki/ca/ca-csr.json <<EOF
{
"CN": "Kubernetes",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "${TLS_C}",
"L": "${TLS_L}",
"O": "Kubernetes",
"OU": "${TLS_OU}",
"ST": "${TLS_ST}"
}
]
}
EOF
cfssl gencert -initca pki/ca/ca-csr.json | cfssljson -bare pki/ca/ca

Generating TLS Certificates

Generate the admin user config files and certificates

cat > pki/admin/admin-csr.json <<EOF
{
"CN": "admin",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "${TLS_C}",
"L": "${TLS_L}",
"O": "system:masters",
"OU": "${TLS_OU}",
"ST": "${TLS_ST}"
}
]
}
EOF
cfssl gencert \
-ca=pki/ca/ca.pem \
-ca-key=pki/ca/ca-key.pem \
-config=pki/ca/ca-config.json \
-profile=kubernetes \
pki/admin/admin-csr.json | cfssljson -bare pki/admin/admin

Generate the worker(s) certs and keys

You can get your EXTERNAL_IP by running this from each worker node. This will force IPv4. If you want to use and have IPv6 then remove -4

curl -s -4 https://ifconfig.co/

For the INTERNAL_IP, you need to change the line with the result of running the following on each worker node (change ens3 if required):

ip addr show ens3 | grep -Po 'inet \K[\d.]+'

If you’re running on a single node or reduced worker nodes, adjust the for loop as appropriate.

NOTE: You will actually need to run the loop manually to ensure you fill out the correct EXTERNAL and INTERNAL IPs —copy and past the loop internals and adjust as required.

for instance in worker-0 worker-1 worker-2; do
cat > pki/clients/${instance}-csr.json <<EOF
{
"CN": "system:node:${instance}",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "${TLS_C}",
"L": "${TLS_L}",
"O": "system:nodes",
"OU": "${TLS_OU}",
"ST": "${TLS_ST}"
}
]
}
EOF
EXTERNAL_IP=$(curl -s -4 https://ifconfig.co)
INTERNAL_IP=$(ip addr show ens3 | grep -Po 'inet \K[\d.]+')
cfssl gencert \
-ca=pki/ca/ca.pem \
-ca-key=pki/ca/ca-key.pem \
-config=pki/ca/ca-config.json \
-hostname=${instance},${INTERNAL_IP},${EXTERNAL_IP} \
-profile=kubernetes \
pki/clients/${instance}-csr.json | cfssljson -bare pki/clients/${instance}
done

Generate the kube-controller-manager cert and key

cat > pki/controller/kube-controller-manager-csr.json <<EOF
{
"CN": "system:kube-controller-manager",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "${TLS_C}",
"L": "${TLS_L}",
"O": "system:kube-controller-manager",
"OU": "${TLS_OU}",
"ST": "${TLS_ST}"
}
]
}
EOF
cfssl gencert \
-ca=pki/ca/ca.pem \
-ca-key=pki/ca/ca-key.pem \
-config=pki/ca/ca-config.json \
-profile=kubernetes \
pki/controller/kube-controller-manager-csr.json | cfssljson -bare pki/controller/kube-controller-manager

Generate the kube-proxy cert and key

cat > pki/proxy/kube-proxy-csr.json <<EOF
{
"CN": "system:kube-proxy",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "${TLS_C}",
"L": "${TLS_L}",
"O": "system:node-proxier",
"OU": "${TLS_OU}",
"ST": "${TLS_ST}"
}
]
}
EOF
cfssl gencert \
-ca=pki/ca/ca.pem \
-ca-key=pki/ca/ca-key.pem \
-config=pki/ca/ca-config.json \
-profile=kubernetes \
pki/proxy/kube-proxy-csr.json | cfssljson -bare pki/proxy/kube-proxy

Generate the scheduler cert and key

cat > pki/scheduler/kube-scheduler-csr.json <<EOF
{
"CN": "system:kube-scheduler",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "${TLS_C}",
"L": "${TLS_L}",
"O": "system:kube-scheduler",
"OU": "${TLS_OU}",
"ST": "${TLS_ST}"
}
]
}
EOF
cfssl gencert \
-ca=pki/ca/ca.pem \
-ca-key=pki/ca/ca-key.pem \
-config=pki/ca/ca-config.json \
-profile=kubernetes \
pki/scheduler/kube-scheduler-csr.json | cfssljson -bare pki/scheduler/kube-scheduler

Generate the api-server cert and key

The KUBERNETES_PUBLIC_ADDRESS will be the IP/VIP/Pool IP of the load balancer that will direct all traffic to the controller nodes. In our case it’s 192.168.0.210.

NOTE: The — ‘hostname=’ line may need adjusting if you have different IPs for your controllers.

KUBERNETES_PUBLIC_ADDRESS=192.168.0.210cat > pki/api/kubernetes-csr.json <<EOF
{
"CN": "kubernetes",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "${TLS_C}",
"L": "${TLS_L}",
"O": "Kubernetes",
"OU": "${TLS_OU}",
"ST": "${TLS_ST}"
}
]
}
EOF
cfssl gencert \
-ca=pki/ca/ca.pem \
-ca-key=pki/ca/ca-key.pem \
-config=pki/ca/ca-config.json \
-hostname=10.32.0.1,192.168.0.110,192.168.0.111,192.168.0.112,${KUBERNETES_PUBLIC_ADDRESS},127.0.0.1,kubernetes.default \
-profile=kubernetes \
pki/api/kubernetes-csr.json | cfssljson -bare pki/api/kubernetes

Generate the service-account cert and key

cat > pki/service-account/service-account-csr.json <<EOF
{
"CN": "service-accounts",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "${TLS_C}",
"L": "${TLS_L}",
"O": "Kubernetes",
"OU": "${TLS_OU}",
"ST": "${TLS_ST}"
}
]
}
EOF
cfssl gencert \
-ca=pki/ca/ca.pem \
-ca-key=pki/ca/ca-key.pem \
-config=pki/ca/ca-config.json \
-profile=kubernetes \
pki/service-account/service-account-csr.json | cfssljson -bare pki/service-account/service-account

Finally, lets move the certs

Copy the files to the controllers and workers as shown below.
If you’re on a single node then simply copy all files listed below for both controllers & workers to the single node.

for instance in worker-0 worker-1 worker-2; do
scp pki/ca/ca.pem pki/clients/${instance}-key.pem pki/clients/${instance}.pem ${instance}:~/
done
for instance in controller-0 controller-1 controller-2; do
scp pki/ca/ca.pem pki/ca/ca-key.pem pki/api/kubernetes-key.pem pki/api/kubernetes.pem pki/service-account/service-account-key.pem pki/service-account/service-account.pem ${instance}:~/
done

Conclusion

You’ve generated all of the certificates required for the kubernetes cluster to work.

Next: Generate kubeconfig files & encryption key

--

--