567 lines
11 KiB
Markdown
567 lines
11 KiB
Markdown
# Provisioning a CA and Generating TLS Certificates
|
|
|
|
In this lab you will provision a [PKI Infrastructure](https://en.wikipedia.org/wiki/Public_key_infrastructure) using CloudFlare's PKI toolkit, [cfssl](https://github.com/cloudflare/cfssl), then use it to bootstrap a Certificate Authority, and generate TLS certificates for the following components: etcd, kube-apiserver, kube-controller-manager, kube-scheduler, kubelet, and kube-proxy.
|
|
|
|
## Certificate Authority
|
|
|
|
In this section you will provision a Certificate Authority that can be used to generate additional TLS certificates.
|
|
|
|
Generate the CA configuration file, certificate, and private key:
|
|
|
|
```
|
|
{
|
|
|
|
cat > ca-config.json <<EOF
|
|
{
|
|
"signing": {
|
|
"default": {
|
|
"expiry": "8760h"
|
|
},
|
|
"profiles": {
|
|
"kubernetes": {
|
|
"usages": ["signing", "key encipherment", "server auth", "client auth"],
|
|
"expiry": "8760h"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
EOF
|
|
|
|
cat > ca-csr.json <<EOF
|
|
{
|
|
"CN": "Kubernetes",
|
|
"key": {
|
|
"algo": "rsa",
|
|
"size": 2048
|
|
},
|
|
"names": [
|
|
{
|
|
"C": "US",
|
|
"L": "Portland",
|
|
"O": "Kubernetes",
|
|
"OU": "CA",
|
|
"ST": "Oregon"
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
|
|
cfssl gencert -initca ca-csr.json | cfssljson -bare ca
|
|
|
|
}
|
|
```
|
|
|
|
Results:
|
|
|
|
```
|
|
ca-key.pem
|
|
ca.pem
|
|
```
|
|
|
|
## Client and Server Certificates
|
|
|
|
In this section you will generate client and server certificates for each Kubernetes component and a client certificate for the Kubernetes `admin` user.
|
|
|
|
### The Admin Client Certificate
|
|
|
|
Generate the `admin` client certificate and private key:
|
|
|
|
```
|
|
{
|
|
|
|
cat > admin-csr.json <<EOF
|
|
{
|
|
"CN": "admin",
|
|
"key": {
|
|
"algo": "rsa",
|
|
"size": 2048
|
|
},
|
|
"names": [
|
|
{
|
|
"C": "US",
|
|
"L": "Portland",
|
|
"O": "system:masters",
|
|
"OU": "Kubernetes The Hard Way",
|
|
"ST": "Oregon"
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
|
|
cfssl gencert \
|
|
-ca=ca.pem \
|
|
-ca-key=ca-key.pem \
|
|
-config=ca-config.json \
|
|
-profile=kubernetes \
|
|
admin-csr.json | cfssljson -bare admin
|
|
|
|
}
|
|
```
|
|
|
|
Results:
|
|
|
|
```
|
|
admin-key.pem
|
|
admin.pem
|
|
```
|
|
|
|
### The Kubelet Client Certificates
|
|
|
|
Kubernetes uses a [special-purpose authorization mode](https://kubernetes.io/docs/admin/authorization/node/) called Node Authorizer, that specifically authorizes API requests made by [Kubelets](https://kubernetes.io/docs/concepts/overview/components/#kubelet). In order to be authorized by the Node Authorizer, Kubelets must use a credential that identifies them as being in the `system:nodes` group, with a username of `system:node:<nodeName>`. In this section you will create a certificate for each Kubernetes worker node that meets the Node Authorizer requirements.
|
|
|
|
Generate a certificate and private key for each Kubernetes worker node:
|
|
|
|
<details open>
|
|
<summary>GCP</summary>
|
|
|
|
```
|
|
for instance in worker-0 worker-1 worker-2; do
|
|
cat > ${instance}-csr.json <<EOF
|
|
{
|
|
"CN": "system:node:${instance}",
|
|
"key": {
|
|
"algo": "rsa",
|
|
"size": 2048
|
|
},
|
|
"names": [
|
|
{
|
|
"C": "US",
|
|
"L": "Portland",
|
|
"O": "system:nodes",
|
|
"OU": "Kubernetes The Hard Way",
|
|
"ST": "Oregon"
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
|
|
EXTERNAL_IP=$(gcloud compute instances describe ${instance} \
|
|
--format 'value(networkInterfaces[0].accessConfigs[0].natIP)')
|
|
|
|
INTERNAL_IP=$(gcloud compute instances describe ${instance} \
|
|
--format 'value(networkInterfaces[0].networkIP)')
|
|
|
|
cfssl gencert \
|
|
-ca=ca.pem \
|
|
-ca-key=ca-key.pem \
|
|
-config=ca-config.json \
|
|
-hostname=${instance},${EXTERNAL_IP},${INTERNAL_IP} \
|
|
-profile=kubernetes \
|
|
${instance}-csr.json | cfssljson -bare ${instance}
|
|
done
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary>AWS</summary>
|
|
|
|
```
|
|
VPC_ID="$(aws ec2 describe-vpcs \
|
|
--filters Name=tag-key,Values=kubernetes.io/cluster/kubernetes-the-hard-way \
|
|
--profile kubernetes-the-hard-way \
|
|
--query 'Vpcs[0].VpcId' \
|
|
--output text)"
|
|
```
|
|
```
|
|
for i in 0 1 2; do
|
|
instance="worker-$i"
|
|
hostname="ip-10-240-0-2$i"
|
|
|
|
cut -c3- >"$instance-csr.json" <<EOF
|
|
{
|
|
"CN": "system:node:$hostname",
|
|
"key": {
|
|
"algo": "rsa",
|
|
"size": 2048
|
|
},
|
|
"names": [
|
|
{
|
|
"C": "US",
|
|
"L": "Portland",
|
|
"O": "system:nodes",
|
|
"OU": "Kubernetes The Hard Way",
|
|
"ST": "Oregon"
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
|
|
INT_EXT_IP="$(aws ec2 describe-instances \
|
|
--filters \
|
|
Name=vpc-id,Values="$VPC_ID" \
|
|
Name=tag:Name,Values="$instance" \
|
|
--profile kubernetes-the-hard-way \
|
|
--query 'Reservations[0].Instances[0].[PrivateIpAddress,PublicIpAddress]' \
|
|
--output text)"
|
|
INTERNAL_IP="$(echo "$INT_EXT_IP"|cut -f1)"
|
|
EXTERNAL_IP="$(echo "$INT_EXT_IP"|cut -f2)"
|
|
|
|
cfssl gencert \
|
|
-ca=ca.pem \
|
|
-ca-key=ca-key.pem \
|
|
-config=ca-config.json \
|
|
-hostname="$hostname,$EXTERNAL_IP,$INTERNAL_IP" \
|
|
-profile=kubernetes \
|
|
"$instance-csr.json"|cfssljson -bare "$instance"
|
|
done
|
|
```
|
|
|
|
</details>
|
|
<p></p>
|
|
|
|
Results:
|
|
|
|
```
|
|
worker-0-key.pem
|
|
worker-0.pem
|
|
worker-1-key.pem
|
|
worker-1.pem
|
|
worker-2-key.pem
|
|
worker-2.pem
|
|
```
|
|
|
|
### The Controller Manager Client Certificate
|
|
|
|
Generate the `kube-controller-manager` client certificate and private key:
|
|
|
|
```
|
|
{
|
|
|
|
cat > kube-controller-manager-csr.json <<EOF
|
|
{
|
|
"CN": "system:kube-controller-manager",
|
|
"key": {
|
|
"algo": "rsa",
|
|
"size": 2048
|
|
},
|
|
"names": [
|
|
{
|
|
"C": "US",
|
|
"L": "Portland",
|
|
"O": "system:kube-controller-manager",
|
|
"OU": "Kubernetes The Hard Way",
|
|
"ST": "Oregon"
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
|
|
cfssl gencert \
|
|
-ca=ca.pem \
|
|
-ca-key=ca-key.pem \
|
|
-config=ca-config.json \
|
|
-profile=kubernetes \
|
|
kube-controller-manager-csr.json | cfssljson -bare kube-controller-manager
|
|
|
|
}
|
|
```
|
|
|
|
Results:
|
|
|
|
```
|
|
kube-controller-manager-key.pem
|
|
kube-controller-manager.pem
|
|
```
|
|
|
|
|
|
### The Kube Proxy Client Certificate
|
|
|
|
Generate the `kube-proxy` client certificate and private key:
|
|
|
|
```
|
|
{
|
|
|
|
cat > kube-proxy-csr.json <<EOF
|
|
{
|
|
"CN": "system:kube-proxy",
|
|
"key": {
|
|
"algo": "rsa",
|
|
"size": 2048
|
|
},
|
|
"names": [
|
|
{
|
|
"C": "US",
|
|
"L": "Portland",
|
|
"O": "system:node-proxier",
|
|
"OU": "Kubernetes The Hard Way",
|
|
"ST": "Oregon"
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
|
|
cfssl gencert \
|
|
-ca=ca.pem \
|
|
-ca-key=ca-key.pem \
|
|
-config=ca-config.json \
|
|
-profile=kubernetes \
|
|
kube-proxy-csr.json | cfssljson -bare kube-proxy
|
|
|
|
}
|
|
```
|
|
|
|
Results:
|
|
|
|
```
|
|
kube-proxy-key.pem
|
|
kube-proxy.pem
|
|
```
|
|
|
|
### The Scheduler Client Certificate
|
|
|
|
Generate the `kube-scheduler` client certificate and private key:
|
|
|
|
```
|
|
{
|
|
|
|
cat > kube-scheduler-csr.json <<EOF
|
|
{
|
|
"CN": "system:kube-scheduler",
|
|
"key": {
|
|
"algo": "rsa",
|
|
"size": 2048
|
|
},
|
|
"names": [
|
|
{
|
|
"C": "US",
|
|
"L": "Portland",
|
|
"O": "system:kube-scheduler",
|
|
"OU": "Kubernetes The Hard Way",
|
|
"ST": "Oregon"
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
|
|
cfssl gencert \
|
|
-ca=ca.pem \
|
|
-ca-key=ca-key.pem \
|
|
-config=ca-config.json \
|
|
-profile=kubernetes \
|
|
kube-scheduler-csr.json | cfssljson -bare kube-scheduler
|
|
|
|
}
|
|
```
|
|
|
|
Results:
|
|
|
|
```
|
|
kube-scheduler-key.pem
|
|
kube-scheduler.pem
|
|
```
|
|
|
|
|
|
### The Kubernetes API Server Certificate
|
|
|
|
The `kubernetes-the-hard-way` static IP address will be included in the list of subject alternative names for the Kubernetes API Server certificate. This will ensure the certificate can be validated by remote clients.
|
|
|
|
Generate the Kubernetes API Server certificate and private key:
|
|
|
|
<details open>
|
|
<summary>GCP</summary>
|
|
|
|
```
|
|
{
|
|
|
|
KUBERNETES_PUBLIC_ADDRESS=$(gcloud compute addresses describe kubernetes-the-hard-way \
|
|
--region $(gcloud config get-value compute/region) \
|
|
--format 'value(address)')
|
|
|
|
cat > kubernetes-csr.json <<EOF
|
|
{
|
|
"CN": "kubernetes",
|
|
"key": {
|
|
"algo": "rsa",
|
|
"size": 2048
|
|
},
|
|
"names": [
|
|
{
|
|
"C": "US",
|
|
"L": "Portland",
|
|
"O": "Kubernetes",
|
|
"OU": "Kubernetes The Hard Way",
|
|
"ST": "Oregon"
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
|
|
cfssl gencert \
|
|
-ca=ca.pem \
|
|
-ca-key=ca-key.pem \
|
|
-config=ca-config.json \
|
|
-hostname=10.32.0.1,10.240.0.10,10.240.0.11,10.240.0.12,${KUBERNETES_PUBLIC_ADDRESS},127.0.0.1,kubernetes.default \
|
|
-profile=kubernetes \
|
|
kubernetes-csr.json | cfssljson -bare kubernetes
|
|
|
|
}
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary>AWS</summary>
|
|
|
|
```
|
|
KUBERNETES_PUBLIC_ADDRESS="$(aws elb describe-load-balancers \
|
|
--load-balancer-name kubernetes-the-hard-way \
|
|
--profile kubernetes-the-hard-way \
|
|
--query 'LoadBalancerDescriptions[0].DNSName' \
|
|
--output text)"
|
|
|
|
cat >kubernetes-csr.json <<EOF
|
|
{
|
|
"CN": "kubernetes",
|
|
"key": {
|
|
"algo": "rsa",
|
|
"size": 2048
|
|
},
|
|
"names": [
|
|
{
|
|
"C": "US",
|
|
"L": "Portland",
|
|
"O": "Kubernetes",
|
|
"OU": "Kubernetes The Hard Way",
|
|
"ST": "Oregon"
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
|
|
cfssl gencert \
|
|
-ca=ca.pem \
|
|
-ca-key=ca-key.pem \
|
|
-config=ca-config.json \
|
|
-hostname=10.32.0.1,10.240.0.10,10.240.0.11,10.240.0.12,ip-10-240-0-10,ip-10-240-0-11,ip-10-240-0-12,$KUBERNETES_PUBLIC_ADDRESS,127.0.0.1,kubernetes.default \
|
|
-profile=kubernetes \
|
|
kubernetes-csr.json|cfssljson -bare kubernetes
|
|
```
|
|
|
|
</details>
|
|
<p></p>
|
|
|
|
Results:
|
|
|
|
```
|
|
kubernetes-key.pem
|
|
kubernetes.pem
|
|
```
|
|
|
|
## The Service Account Key Pair
|
|
|
|
The Kubernetes Controller Manager leverages a key pair to generate and sign service account tokens as describe in the [managing service accounts](https://kubernetes.io/docs/admin/service-accounts-admin/) documentation.
|
|
|
|
Generate the `service-account` certificate and private key:
|
|
|
|
```
|
|
{
|
|
|
|
cat > service-account-csr.json <<EOF
|
|
{
|
|
"CN": "service-accounts",
|
|
"key": {
|
|
"algo": "rsa",
|
|
"size": 2048
|
|
},
|
|
"names": [
|
|
{
|
|
"C": "US",
|
|
"L": "Portland",
|
|
"O": "Kubernetes",
|
|
"OU": "Kubernetes The Hard Way",
|
|
"ST": "Oregon"
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
|
|
cfssl gencert \
|
|
-ca=ca.pem \
|
|
-ca-key=ca-key.pem \
|
|
-config=ca-config.json \
|
|
-profile=kubernetes \
|
|
service-account-csr.json | cfssljson -bare service-account
|
|
|
|
}
|
|
```
|
|
|
|
Results:
|
|
|
|
```
|
|
service-account-key.pem
|
|
service-account.pem
|
|
```
|
|
|
|
|
|
## Distribute the Client and Server Certificates
|
|
|
|
Copy the appropriate certificates and private keys to each worker instance:
|
|
|
|
<details open>
|
|
<summary>GCP</summary>
|
|
|
|
```
|
|
for instance in worker-0 worker-1 worker-2; do
|
|
gcloud compute scp ca.pem ${instance}-key.pem ${instance}.pem ${instance}:~/
|
|
done
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary>AWS</summary>
|
|
|
|
```
|
|
get_ip() {
|
|
aws ec2 describe-instances \
|
|
--filters \
|
|
Name=vpc-id,Values="$VPC_ID" \
|
|
Name=tag:Name,Values="$1" \
|
|
--profile kubernetes-the-hard-way \
|
|
--query 'Reservations[0].Instances[0].PublicIpAddress' \
|
|
--output text
|
|
}
|
|
```
|
|
```
|
|
for instance in worker-0 worker-1 worker-2; do
|
|
scp -i ~/.ssh/kubernetes-the-hard-way -o StrictHostKeyChecking=no \
|
|
ca.pem "$instance-key.pem" "$instance.pem" "ubuntu@$(get_ip "$instance"):~/"
|
|
done
|
|
```
|
|
|
|
</details>
|
|
<p></p>
|
|
|
|
Copy the appropriate certificates and private keys to each controller instance:
|
|
|
|
<details open>
|
|
<summary>GCP</summary>
|
|
|
|
```
|
|
for instance in controller-0 controller-1 controller-2; do
|
|
gcloud compute scp ca.pem ca-key.pem kubernetes-key.pem kubernetes.pem \
|
|
service-account-key.pem service-account.pem ${instance}:~/
|
|
done
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary>AWS</summary>
|
|
|
|
```
|
|
for instance in controller-0 controller-1 controller-2; do
|
|
scp -i ~/.ssh/kubernetes-the-hard-way -o StrictHostKeyChecking=no \
|
|
ca.pem ca-key.pem kubernetes-key.pem kubernetes.pem service-account-key.pem service-account.pem \
|
|
"ubuntu@$(get_ip "$instance"):~/"
|
|
done
|
|
```
|
|
|
|
</details>
|
|
<p></p>
|
|
|
|
> The `kube-proxy`, `kube-controller-manager`, `kube-scheduler`, and `kubelet` client certificates will be used to generate client authentication configuration files in the next lab.
|
|
|
|
Next: [Generating Kubernetes Configuration Files for Authentication](05-kubernetes-configuration-files.md)
|