Configure DNS SAN Certificates with Citadel#
This guide describes the process of consuming Aspen Mesh certificates by Gateways to terminate TLS traffic coming from end user clients. By default, all Aspen Mesh generated certificates only include SPIFFE URI in SAN which is not compatible with end user clients like browsers or curl. Adding DNS SAN entries in certificates enables you to terminate TLS traffic coming from browsers and other end user clients at Aspen Mesh gateways.
Warning
Use this guide Configure Gateways to use Aspen Mesh minted certificates for using this functionality.
Prerequisites#
DNS name configured to point to the cluster via A, ALIAS, or CNAME records
ServiceAccount configured for the service that should have the DNS added to the certificate as we generate DNS certificates triggered based on service account existence.
jq installed
Setup#
In the override values file for the Aspen Mesh install, add the following global values
global: customDnsNames: - serviceAccountName: httpbin serviceAccountNamespace: default dnsName: test.example.com
Use the ServiceAccount and DNS name configured in the prerequisistes for the
serviceAccountName/NamespaceanddnsNamevalues.Deploy the new values to the cluster via a
helm upgrade. If the ServiceAccount exists before the upgrade, the secret generated by Citadel must be deleted in order to refresh the certificate with the new DNS name.Save the following script to translate the Citadel certificate into a
kubernetes.io/tlscertificate for use in the Aspen Mesh Gateway asingress-gw.sh. This step is needed as the keys in secret generated by Citadel and expected keys in secrets referenced by Gateway resource are different.#!/bin/bash set -eEuo pipefail OLD_SECRET=istio.$1 KEY=$(kubectl -n $2 get secret $OLD_SECRET -o json | jq '.data | ."key.pem"') CERT=$(kubectl -n $2 get secret $OLD_SECRET -o json | jq '.data | ."cert-chain.pem"') ROOT_CERT=$(kubectl -n $2 get secret $OLD_SECRET -o json | jq '.data | ."root-cert.pem"') NEW_SECRET=$1-certs kubectl apply -n istio-system -f - << EOF apiVersion: v1 kind: Secret metadata: name: ${NEW_SECRET} data: key: ${KEY} cert: ${CERT} cacert: ${ROOT_CERT} type: kubernetes.io/generic EOF
Usage:
./ingress-gw.sh <SERVICE_ACCOUNT_NAME> <SERVICE_ACCOUNT_NAMESPACE>This script will generate a new secret in the
istio-systemnamespace calledSERVICE_ACCOUNT_NAME-certswhereSERVICE_ACCOUNT_NAMEis the name of the ServiceAccount used in the above override values file.Create the Istio Gateway using the new secret name for TLS configuration
apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: dns-certs-gateway spec: selector: istio: ingressgateway servers: - port: number: 443 name: https protocol: HTTPS hosts: - <DNS_NAME> tls: mode: <MUTUAL|SIMPLE> credentialName: <SERVICE_ACCOUNT_NAME>-certs
Create the VirtualService associated with the service you wish to expose and connect it to the Gateway. An example can be found in the Example Deployment section of this document.
Add the Citadel root certificate to your OS’s root certificate authorities. The following commands will extract the root certificate into a file called
root.crtin your current directory. Make sure you can runkubectlcommands against the cluster.Mac
$ kubectl get cm istio-security -n istio-system -ojsonpath={$.data.caTLSRootCert} | base64 -D > root.crt`
Adding the certificate to the root store
$ sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain root.crt`
Linux
$ kubectl get cm istio-security -n istio-system -ojsonpath={$.data.caTLSRootCert} | base64 -D > root.crt`
Adding the certificate to the root store
Refer to the Linux distribution’s instructions on adding a new root CA
Windows
> kubectl get cm istio-security -n istio-system -ojsonpath={$.data.caTLSRootCert} > root.b64 > certutil -decode root.b64 root.crt > del root.b64
Adding the certificate to the root store
In an Administrative PowerShell window
Import-Certificate -FilePath "root.crt" -CertStoreLocation Cert:\LocalMachine\Root
Note
Windows may have trouble connecting to the Gateway if it is configured with Mutual TLS
Certificate generation using the script is currently a manual process. If a new certificate is generated or the certificate is rotated, the
ingress-gw.shshould be run again to refresh the Gateway certificate.
Example Deployment#
Here we will deploy a copy of httpbin from the samples directory in the Aspen Mesh install and expose it using the
certificate generated from its service account. These steps assume that Aspen Mesh has been installed into the cluster
and that the current directory is the root of the untar’d installer.
Enable Istio Sidecar injection in the default namespace
$ kubectl label namespace default istio-injection=enabled
Deploy the
httpbinsample application$ kubectl apply -f samples/httpbin/httpbin.yaml
Verify that a Aspen Mesh Citadel secret is created for the service account
$ kubectl get secret istio.httpbin -n default NAME TYPE DATA AGE istio.httpbin istio.io/key-and-cert 3 1m
Add the custom DNS name to your override values file, replacing
test.example.comwith your own DNS name.global: customDnsNames: - serviceAccountName: httpbin serviceAccountNamespace: default dnsName: test.example.com
Deploy the values using helm upgrade to apply the changes
$ helm upgrade istiod manifests/charts/istio-control/istio-discovery \ --namespace=istio-system \ --values=YOUR_OVERRIDE_VALUES_FILE \ --values=values.yaml
Delete the
istio.httpbinsecret to force Citadel to regenerate the certificate with the specified DNS name.$ kubectl delete secret istio.httpbin -n default
Use the
ingress-gw.shto create the Istio Gateway certificate secret$ ./ingress-gw.sh httpbin default
Deploy the following Gateway and VirtualService to your cluster replacing
test.example.comwith your own DNS nameapiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: httpbin-gateway namespace: default spec: selector: istio: ingressgateway servers: - port: number: 443 name: https protocol: HTTPS hosts: - "test.example.com" tls: mode: SIMPLE credentialName: httpbin-certs --- apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: httpbin namespace: default spec: hosts: - "*" gateways: - httpbin-gateway http: - route: - destination: host: httpbin port: number: 8000
Observe that navigating to the dns name provided above now generates an error when connecting, stating that the root certificate is not trusted.
Follow the instructions in step 6 from the previous instructions to trust the root CA from the cluster
Observe that now the connection is trusted and you can see the
httpbinpage when navigating to the DNS name.