Skip to content

Instantly share code, notes, and snippets.

@Vertiwell
Last active March 17, 2022 09:07
Show Gist options
  • Save Vertiwell/2243ca66e9ac156bbb5f077888db6083 to your computer and use it in GitHub Desktop.
Save Vertiwell/2243ca66e9ac156bbb5f077888db6083 to your computer and use it in GitHub Desktop.
Cert-Manager CA
#!/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