Last active
March 17, 2022 09:07
-
-
Save Vertiwell/2243ca66e9ac156bbb5f077888db6083 to your computer and use it in GitHub Desktop.
Cert-Manager CA
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
### Deploying Cert-Manager on Kubernetes for Debian/Ubuntu based OS | |
## Baseline Guide: https://cert-manager.io/docs/installation/helm/ | |
# Type of Deployment: Helm | |
# | |
### Minimum Requirements ### | |
## Three Worker Node Cluster (Tested on K0s, K3s, K8s) | |
# | |
## The following base packages are required: | |
# Helm, Package Manager | |
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 && \ | |
chmod 700 get_helm.sh && \ | |
./get_helm.sh && \ | |
# | |
echo "Provide a domain name to use for browsing to (i.e: example.com):" | |
read DOMAIN | |
### Build the CA Certificates ### | |
#### Creating a Root CA - https://jamielinux.com/docs/openssl-certificate-authority/ | |
### This will require a password, don't lose it | |
## It is recommended that this be done on an airgapped cluster as best practice and the intermediate cert be transferred off to be used in the production environment | |
## Variables to be used - Please double quote any strings with spaces for the CA | |
# Set a domain to use (needs to be real if you want to access this externally from the internet) | |
echo "Provide a country code (Recommend: GB):" | |
read COUNTRY | |
echo "Provide a State (Recommend: New South Wales):" | |
read STATE | |
echo "Provide a City (Recommend: London):" | |
read CITY | |
echo "Provide a Department (Recommend: IT):" | |
read DEPARTMENT | |
echo "Provide an Email (Recommend: myemail@example.com):" | |
read EMAIL | |
echo "Provide a Certificate Authority Name (Recommend: Company Pty Ltd):" | |
read COMMONNAME | |
## Setup directory structure and database file for OpenSSL | |
mkdir /root/ca; mkdir /root/ca/certs /root/ca/crl /root/ca/newcerts /root/ca/private; chmod 700 /root/ca/private && touch /root/ca/index.txt && echo 1000 > /root/ca/serial && \ | |
## Setup configuration to be used by OpenSSL | |
cat <<EOF >/root/ca/openssl.cnf | |
[ ca ] | |
default_ca = CA_default | |
[ CA_default ] | |
dir = /root/ca | |
certs = /root/ca/certs | |
crl_dir = /root/ca/crl | |
new_certs_dir = /root/ca/newcerts | |
database = /root/ca/index.txt | |
serial = /root/ca/serial | |
RANDFILE = /root/ca/private/.rand | |
private_key = /root/ca/private/ca.key.pem | |
certificate = /root/ca/certs/ca.cert.pem | |
crlnumber = /root/ca/crlnumber | |
crl = /root/ca/crl/ca.crl.pem | |
crl_extensions = crl_ext | |
default_crl_days = 900 | |
default_md = sha256 | |
name_opt = ca_default | |
cert_opt = ca_default | |
default_days = 375 | |
preserve = no | |
policy = policy_strict | |
[ policy_strict ] | |
countryName = match | |
stateOrProvinceName = match | |
organizationName = match | |
organizationalUnitName = optional | |
commonName = supplied | |
emailAddress = optional | |
[ req ] | |
default_bits = 2048 | |
distinguished_name = req_distinguished_name | |
string_mask = utf8only | |
default_md = sha256 | |
x509_extensions = v3_ca | |
[ req_distinguished_name ] | |
countryName = Country Name (2 letter code) | |
stateOrProvinceName = State or Province Name | |
localityName = Locality Name | |
0.organizationName = Organization Name | |
organizationalUnitName = Organizational Unit Name | |
commonName = Common Name | |
emailAddress = Email Address | |
[ v3_ca ] | |
subjectKeyIdentifier = hash | |
authorityKeyIdentifier = keyid:always,issuer | |
basicConstraints = critical, CA:true | |
keyUsage = critical, digitalSignature, cRLSign, keyCertSign | |
[ v3_intermediate_ca ] | |
subjectKeyIdentifier = hash | |
authorityKeyIdentifier = keyid:always,issuer | |
basicConstraints = critical, CA:true, pathlen:0 | |
keyUsage = critical, digitalSignature, cRLSign, keyCertSign | |
[ usr_cert ] | |
basicConstraints = CA:FALSE | |
nsCertType = client, email | |
nsComment = "OpenSSL Generated Client Certificate" | |
subjectKeyIdentifier = hash | |
authorityKeyIdentifier = keyid,issuer | |
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment | |
extendedKeyUsage = clientAuth, emailProtection | |
[ server_cert ] | |
basicConstraints = CA:FALSE | |
nsCertType = server | |
nsComment = "OpenSSL Generated Server Certificate" | |
subjectKeyIdentifier = hash | |
authorityKeyIdentifier = keyid,issuer:always | |
keyUsage = critical, digitalSignature, keyEncipherment | |
extendedKeyUsage = serverAuth | |
[ crl_ext ] | |
authorityKeyIdentifier=keyid:always | |
[ ocsp ] | |
basicConstraints = CA:FALSE | |
subjectKeyIdentifier = hash | |
authorityKeyIdentifier = keyid,issuer | |
keyUsage = critical, digitalSignature | |
extendedKeyUsage = critical, OCSPSigning | |
EOF | |
## Generate Root Key - Requires a secure password, do not automate | |
openssl genrsa -aes256 -out /root/ca/private/ca.key.pem 4096 && \ | |
## Set file permissions of Root Key | |
chmod 400 /root/ca/private/ca.key.pem && \ | |
## Generate Root Certificate from Root Key (30 years) | |
openssl req -config /root/ca/openssl.cnf -key /root/ca/private/ca.key.pem -new -x509 -days 10950 -sha256 -extensions v3_ca -out /root/ca/certs/ca.cert.pem -subj "/emailAddress=$EMAIL/C=$COUNTRY/ST=$STATE/L=$CITY/O=$DEPARTMENT/CN=$COMMONNAME" && \ | |
## Set file permissions of Root Certificate | |
chmod 444 /root/ca/certs/ca.cert.pem && \ | |
## Verify Root Certificate | |
openssl x509 -noout -text -in /root/ca/certs/ca.cert.pem && \ | |
### Creating an Intermediate Certificate (Signs certificates on behalf of the root CA) | |
## Setup directory structure and database file for OpenSSL | |
mkdir /root/ca/intermediate; mkdir /root/ca/intermediate/certs /root/ca/intermediate/crl /root/ca/intermediate/csr /root/ca/intermediate/newcerts /root/ca/intermediate/private; chmod 700 /root/ca/intermediate/private && touch /root/ca/intermediate/index.txt && echo 1000 > /root/ca/intermediate/serial && \ | |
# Create a CRL file to keep track of certificate revocation lists | |
echo 1000 > /root/ca/intermediate/crlnumber && \ | |
## Setup Intermediate configuration to be used by OpenSSL | |
cat <<EOF >/root/ca/intermediate/openssl.cnf | |
[ ca ] | |
default_ca = CA_default | |
[ CA_default ] | |
dir = /root/ca/intermediate | |
certs = /root/ca/intermediate/certs | |
crl_dir = /root/ca/intermediate/crl | |
new_certs_dir = /root/ca/intermediate/newcerts | |
database = /root/ca/intermediate/index.txt | |
serial = /root/ca/intermediate/serial | |
RANDFILE = /root/ca/intermediate/private/.rand | |
private_key = /root/ca/intermediate/private/intermediate.key.pem | |
certificate = /root/ca/intermediate/certs/intermediate.cert.pem | |
crlnumber = /root/ca/intermediate/crlnumber | |
crl = /root/ca/intermediate/crl/intermediate.crl.pem | |
crl_extensions = crl_ext | |
default_crl_days = 900 | |
default_md = sha256 | |
name_opt = ca_default | |
cert_opt = ca_default | |
default_days = 375 | |
preserve = no | |
policy = policy_loose | |
[ policy_loose ] | |
countryName = optional | |
stateOrProvinceName = optional | |
localityName = optional | |
organizationName = optional | |
organizationalUnitName = optional | |
commonName = supplied | |
emailAddress = optional | |
[ req ] | |
default_bits = 2048 | |
distinguished_name = req_distinguished_name | |
string_mask = utf8only | |
default_md = sha256 | |
x509_extensions = v3_ca | |
[ req_distinguished_name ] | |
countryName = Country Name (2 letter code) | |
stateOrProvinceName = State or Province Name | |
localityName = Locality Name | |
0.organizationName = Organization Name | |
organizationalUnitName = Organizational Unit Name | |
commonName = Common Name | |
emailAddress = Email Address | |
[ v3_ca ] | |
subjectKeyIdentifier = hash | |
authorityKeyIdentifier = keyid:always,issuer | |
basicConstraints = critical, CA:true | |
keyUsage = critical, digitalSignature, cRLSign, keyCertSign | |
[ v3_intermediate_ca ] | |
subjectKeyIdentifier = hash | |
authorityKeyIdentifier = keyid:always,issuer | |
basicConstraints = critical, CA:true, pathlen:0 | |
keyUsage = critical, digitalSignature, cRLSign, keyCertSign | |
[ usr_cert ] | |
basicConstraints = CA:FALSE | |
nsCertType = client, email | |
nsComment = "OpenSSL Generated Client Certificate" | |
subjectKeyIdentifier = hash | |
authorityKeyIdentifier = keyid,issuer | |
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment | |
extendedKeyUsage = clientAuth, emailProtection | |
[ server_cert ] | |
basicConstraints = CA:FALSE | |
nsCertType = server | |
nsComment = "OpenSSL Generated Server Certificate" | |
subjectKeyIdentifier = hash | |
authorityKeyIdentifier = keyid,issuer:always | |
keyUsage = critical, digitalSignature, keyEncipherment | |
extendedKeyUsage = serverAuth | |
[ crl_ext ] | |
authorityKeyIdentifier=keyid:always | |
[ ocsp ] | |
basicConstraints = CA:FALSE | |
subjectKeyIdentifier = hash | |
authorityKeyIdentifier = keyid,issuer | |
keyUsage = critical, digitalSignature | |
extendedKeyUsage = critical, OCSPSigning | |
EOF | |
## Generate Intermediate Key | |
openssl genrsa -aes256 -out /root/ca/intermediate/private/intermediate.key.pem 4096 && \ | |
## Set file permissions of Intermediate Key | |
chmod 400 /root/ca/intermediate/private/intermediate.key.pem && \ | |
## Generate Intermediate CSR from Intermediate Key (10 years) | |
openssl req -config /root/ca/intermediate/openssl.cnf -new -sha256 -key /root/ca/intermediate/private/intermediate.key.pem -out /root/ca/intermediate/csr/intermediate.csr.pem -subj "/emailAddress=$EMAIL/C=$COUNTRY/ST=$STATE/L=$CITY/O=$DEPARTMENT/CN=$COMMONNAME Intermediate CA" && \ | |
## Generate Intermediate Certificate signed by the Root CA (10 years) | |
openssl ca -config /root/ca/openssl.cnf -extensions v3_intermediate_ca -days 3650 -notext -md sha256 -in /root/ca/intermediate/csr/intermediate.csr.pem -out /root/ca/intermediate/certs/intermediate.cert.pem && \ | |
## Set file permissions of the Intermediate Certificate | |
chmod 444 /root/ca/intermediate/certs/intermediate.cert.pem && \ | |
## Verify the Intermediate Certificate | |
openssl x509 -noout -text -in /root/ca/intermediate/certs/intermediate.cert.pem && \ | |
## Verify the Intermediate Certificate against the Root Certificate | |
openssl verify -CAfile /root/ca/certs/ca.cert.pem /root/ca/intermediate/certs/intermediate.cert.pem && \ | |
## Create the Certificate Chain File | |
cat /root/ca/intermediate/certs/intermediate.cert.pem /root/ca/certs/ca.cert.pem > /root/ca/intermediate/certs/ca-chain.cert.pem && \ | |
## Set file permissions of the Certificate Chain File | |
chmod 444 /root/ca/intermediate/certs/ca-chain.cert.pem && \ | |
## Create the CRL | |
openssl ca -gencrl -config /root/ca/intermediate/openssl.cnf -out /root/ca/intermediate/crl/intermediate.crl | |
## External certs setup ## | |
### Installation Cert-Manager### | |
# | |
# Add the Helm Chart | |
helm repo add jetstack https://charts.jetstack.io && helm repo update && \ | |
# Helm Chart Values Cert-Manager: https://github.com/jetstack/cert-manager/blob/master/deploy/charts/cert-manager/values.yaml | |
# Install the Helm Chart for Cert-Manager | |
helm install cert-manager jetstack/cert-manager --set installCRDs=true -n cert-manager --create-namespace && \ | |
## Certificate Revocation List: https://github.com/bitnami/charts/tree/master/bitnami/apache | |
kubectl create configmap crl-file -n cert-manager --from-file=/root/ca/intermediate/crl/intermediate.crl | |
helm repo add bitnami https://charts.bitnami.com/bitnami | |
helm install apache bitnami/apache -n cert-manager --set htdocsConfigMap=crl-file --set service.type=ClusterIP | |
# Unencrypt the private key with the previously used password to protect the CA | |
openssl rsa -in /root/ca/intermediate/private/intermediate.key.pem -out /root/ca/intermediate/private/key.unencrypted.pem && \ | |
# Create a secret in the Kubernetes cluster that stores the certificate chain and key | |
TLSCRT=$(cat /root/ca/intermediate/certs/ca-chain.cert.pem | base64 -w0) | |
TLSKEY=$(cat /root/ca/intermediate/private/key.unencrypted.pem | base64 -w0) | |
cat <<EOF >ca-keypair.yaml | |
apiVersion: v1 | |
kind: Secret | |
metadata: | |
name: ca-keypair | |
namespace: cert-manager | |
data: | |
tls.crt: $TLSCRT | |
tls.key: $TLSKEY | |
EOF | |
kubectl apply -f ca-keypair.yaml && \ | |
# Delete the unencrypted key | |
rm /root/ca/intermediate/private/key.unencrypted.pem && \ | |
# Create a Cluster Issuer that uses the self-signed certificate | |
cat <<EOF >cmself-signed.yaml | |
apiVersion: cert-manager.io/v1 | |
kind: ClusterIssuer | |
metadata: | |
name: ca-issuer | |
namespace: cert-manager | |
spec: | |
ca: | |
secretName: ca-keypair | |
crlDistributionPoints: | |
- "http://crl.$DOMAIN/intermediate.crl" | |
EOF | |
# Apply the Issuer | |
kubectl apply -f cmself-signed.yaml && \ | |
# Wait until Cert-Manager is running before moving on | |
ROLLOUT_STATUS_CMD="kubectl rollout status -w --timeout=300s deployment/cert-manager-webhook -n cert-manager" | |
until $ROLLOUT_STATUS_CMD || [ $n -eq 300 ]; do | |
$ROLLOUT_STATUS_CMD | |
n=$((n + 1)) | |
sleep 5 | |
done | |
# Create the IngressRoute to direct traffic to your application | |
cat <<EOF >crl-ingress.yaml | |
apiVersion: traefik.containo.us/v1alpha1 | |
kind: IngressRoute | |
metadata: | |
name: crl-ingress | |
namespace: cert-manager | |
spec: | |
entryPoints: | |
- websecure | |
routes: | |
- match: Host(\`crl.$DOMAIN\`) && PathPrefix(\`/\`) | |
kind: Rule | |
services: | |
- name: apache | |
port: 80 | |
tls: | |
secretName: crl-cert | |
EOF | |
# Apply the certificate and Ingress for the CRL | |
kubectl apply -f crl-ingress.yaml && \ | |
# Apply the certificate to the OS so it can be trusted | |
openssl x509 -in /root/ca/intermediate/certs/ca-chain.cert.pem -out /usr/local/share/ca-certificates/self-signed-cert.crt | |
update-ca-certificates && \ | |
# For pods created by the user, you can add volumes from config maps to trust the certificates | |
kubectl create configmap ca-store --from-file=/usr/local/share/ca-certificates/self-signed-cert.crt && \ | |
# In order to apply this CA cert across worker nodes (so it can be trusted) a Daemonset that engages with the underlying OS (Ubuntu in this case) must be deployed | |
cat <<EOF >custom-ca-installer.yaml | |
apiVersion: v1 | |
kind: ConfigMap | |
metadata: | |
name: trusted-ca | |
data: | |
ca.crt: |+ | |
$(cat /usr/local/share/ca-certificates/self-signed-cert.crt | sed 's/^/ /') | |
--- | |
apiVersion: v1 | |
kind: ConfigMap | |
metadata: | |
name: setup-script | |
data: | |
setup.sh: | | |
echo "\$TRUSTED_CERT" > /usr/local/share/ca-certificates/ca.crt && update-ca-certificates; KTYPE=$(kubectl get nodes -o jsonpath="{.items[].status.nodeInfo.kubeletVersion}" | tail -c 3); if [[ "${KTYPE}" == *"k3s"* ]]; then systemctl restart k3s-agent; fi; if [[ "${KTYPE}" == *"k8s"* ]]; then systemctl restart containerd; fi; if [[ "${KTYPE}" == *"k0s"* ]]; then systemctl restart k0sworker; fi | |
--- | |
apiVersion: apps/v1 | |
kind: DaemonSet | |
metadata: | |
name: custom-ca-installation | |
labels: | |
app: custom-ca-installation | |
spec: | |
selector: | |
matchLabels: | |
app: custom-ca-installation | |
template: | |
metadata: | |
labels: | |
app: custom-ca-installation | |
spec: | |
hostNetwork: true | |
hostPID: true | |
initContainers: | |
- name: ca-installation | |
command: | |
- nsenter | |
- --mount=/proc/1/ns/mnt | |
- -- | |
- bash | |
- -c | |
args: ["\$(SETUP_SCRIPT)"] | |
image: debian | |
env: | |
- name: TRUSTED_CERT | |
valueFrom: | |
configMapKeyRef: | |
name: trusted-ca | |
key: ca.crt | |
- name: SETUP_SCRIPT | |
valueFrom: | |
configMapKeyRef: | |
name: setup-script | |
key: setup.sh | |
securityContext: | |
privileged: true | |
containers: | |
- name: sleep | |
image: k8s.gcr.io/pause:3.1 | |
updateStrategy: | |
type: RollingUpdate | |
EOF | |
# Apply this Daemonset | |
kubectl apply -f custom-ca-installer.yaml && \ | |
# Wait for it to be applied | |
kubectl rollout status -w --timeout=300s daemonset/custom-ca-installation && sleep 10 && \ | |
# Inform to install on local computer | |
echo "Install CA certs on your local machine: cat /usr/local/share/ca-certificates/self-signed-cert.crt" | |
# Cleanup | |
kubectl delete -f custom-ca-installer.yaml; rm cmself* custom-ca-installer.yaml >/dev/null 2>&1 | |
# Wipe everything: kubectl delete ns cert-manager; rm -r /root/ca |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment