# Configuring Let's Encrypt certificate on attached clusters This is a guide on how to configure attached cluster with Let's Encrypt issued certificate for Traefik, so that the cluster is correctly recognized and configured by the management cluster. Basic configuration variables: ```sh # Path to management cluster kubeconfig file export KUBECONFIG=/path/to/the/management-cluster-kubeconfig # Workspace name where the new cluster will be attached export WORKSPACE_NAMESPACE=acme # Name of the newly atttached cluster export ATTACHED_CLUSTER_NAME=acme-test # DNS name that will be used for attached cluster export CLUSTER_DOMAIN_NAME="attached.ksphere-platform.d2iq.cloud" ``` ## Attach the cluster without the Let's Encrypt certificate **CLUSTER CONTEXT:** `management` Create new workspacels and attach a cluster: ```sh ./dkp create workspace $WORKSPACE_NAMESPACE -n $WORKSPACE_NAMESPACE ./dkp attach cluster -n "$ATTACHED_CLUSTER_NAME" --attached-kubeconfig /path/to/kubeconfig -w $WORKSPACE_NAMESPACE ``` ## Apply a ClusterIssuer to your attached cluster **CLUSTER CONTEXT:** `attached` *NOTE:* Reuse the cluster issuer from your management cluster if you are using the same base DNS name. Create a new ACME `ClusterIssuer` for Let's Encrypt certificate. This particular `ClusterIssuer` is configured to complete ACME challenge `DNS01` challenge. ```sh export ACME_EMAIL=kommander-e2e-tests@d2iq.com export AWS_REGION=us-west-2 export EXTERNAL_DNS_ROUTE53_ZONE_ID=Z36CN7WXPDV8VR export EXTERNAL_DNS_AWS_ASSUME_ROLE=arn:aws:iam::999867407951:role/cert-manager-dns01 cat << EOF | kubectl apply -f - --- apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: kommander-acme-issuer spec: acme: email: "${ACME_EMAIL}" server: https://acme-v02.api.letsencrypt.org/directory privateKeySecretRef: name: kommander-acme-issuer-account solvers: - dns01: route53: region: "${AWS_REGION}" hostedZoneID: "${EXTERNAL_DNS_ROUTE53_ZONE_ID}" role: "${EXTERNAL_DNS_AWS_ASSUME_ROLE}" EOF ``` ## Disable Kommander automatically created certificate to update secret containing the certificate. **CLUSTER:** `attached` *NOTE:* This is a same operation that `kommander-cli` does on `management` cluster when installed with Let's Encrypt certificate configuration. ```sh cat << EOF | kubectl -n $WORKSPACE_NAMESPACE patch certificate kommander-traefik --type='merge' --patch-file=/dev/stdin --- spec: issuerRef: name: no-op EOF ``` Create a new traefik `Certificate` issued by the newly created ACME issuer: ```sh cat << EOF | kubectl apply -f - apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: kommander-traefik-acme namespace: ${WORKSPACE_NAMESPACE} spec: secretName: kommander-traefik-certificate duration: 19200h # 800d commonName: "${CLUSTER_DOMAIN_NAME}" isCA: false usages: - server auth dnsNames: - "${CLUSTER_DOMAIN_NAME}" issuerRef: name: kommander-acme-issuer kind: ClusterIssuer EOF ``` Configure new domain name and certificate in `konvoyconfig-kubeaddons` on the attached cluster. ```sh cat << EOF | kubectl apply -f - apiVersion: v1 kind: ConfigMap metadata: name: konvoyconfig-kubeaddons namespace: ${WORKSPACE_NAMESPACE} data: clusterHostname: "${CLUSTER_DOMAIN_NAME}" caSecretName: kommander-traefik-certificate EOF ``` ## Trigger update on management cluster **CLUSTER:** `management` This will manually trigger the updates on the management cluster that will reconfigure its components to use the newly configured domain on attached cluster. ```sh cat << EOF | kubectl -n $WORKSPACE_NAMESPACE patch kommandercluster $ATTACHED_CLUSTER_NAME --type='merge' --patch-file=/dev/stdin --- metadata: annotations: d2iq-update: "`date +%s`" EOF ``` DNS per attached cluster # Per cluster External DNS `external-dns` is a component that manages DNS records in public DNS providers (like AWS Route53) and creates a DNS records for `LoadBalancer` services based on the configured annotations on the `Service` objects. This feature is useful when provisioning clusters with custom domains that should be automatically managed by the cluster itself. This document explains on how to create a unique domain configuration per each cluster in a single Kommander Workspace. ## Unique DNS domain for each cluster **CLUSTER:** `management` Create Traefik configuration override in the workspace. Having these overrides allows to deploy per cluster configuration for `external-dns` application. These commands should be executed only once per Workspace. ```sh # Workspace name where the clusters are attached. export WORKSPACE_NAMESPACE=acme ``` ```sh cat << EOF | kubectl -n $WORKSPACE_NAMESPACE patch appdeployment traefik --type='merge' --patch-file=/dev/stdin spec: configOverrides: name: traefik-config-overrides EOF export TRAEFIK_UID=$(kubectl -n $WORKSPACE_NAMESPACE get appdeployment traefik -o jsonpath='{.metadata.uid}') cat << EOF | kubectl apply -f - apiVersion: v1 data: values.yaml: '# NOOP' kind: ConfigMap metadata: labels: apps.kommander.mesosphere.io/federated-config-map-name: traefik-overrides name: traefik-config-overrides namespace: ${WORKSPACE_NAMESPACE} ownerReferences: - apiVersion: apps.kommander.d2iq.io/v1alpha2 kind: AppDeployment name: traefik uid: ${TRAEFIK_UID} EOF ``` Configuring unique domain name per every attached cluster. ```sh export CLUSTER_DOMAIN_NAME="attached.ksphere-platform.d2iq.cloud" export ATTACHED_CLUSTER_NAME="cluster-01" cat << EOF | kubectl -n $WORKSPACE_NAMESPACE patch fcm traefik-overrides --type="merge" --patch-file=/dev/stdin --- spec: overrides: - clusterName: "${ATTACHED_CLUSTER_NAME}" clusterOverrides: - path: "/data" value: values.yaml: | service: annotations: external-dns.alpha.kubernetes.io/hostname: "${CLUSTER_DOMAIN_NAME}" EOF ``` ## External DNS **CLUSTER:** `management` Create `external-dns` `AppDeployment` in the workspace. This will deploy the `external-dns` each cluster in the workspace. ```sh export EXTERNAL_DNS_VERSION=6.1.8 cat << EOF | kubectl apply -f - apiVersion: apps.kommander.d2iq.io/v1alpha2 kind: AppDeployment metadata: name: external-dns namespace: ${WORKSPACE_NAMESPACE} spec: appRef: kind: ClusterApp name: external-dns-${EXTERNAL_DNS_VERSION} configOverrides: name: external-dns-config-overrides EOF ``` Applying this configuration assumes that every attached cluster can assume given AWS IAM role and will manage DNS records in the shared DNS zone. ```sh export EXTERNAL_DNS_AWS_REGION=us-west-2 export EXTERNAL_DNS_AWS_ASSUME_ROLE=arn:aws:iam::999867407951:role/cert-manager-dns01 export EXTERNAL_DNS_DOMAIN_FILTER=ksphere-platform.d2iq.cloud export EXTERNAL_DNS_UID=$(kubectl -n $WORKSPACE_NAMESPACE get appdeployment external-dns -o jsonpath='{.metadata.uid}') cat << EOF | kubectl apply -f - apiVersion: v1 kind: ConfigMap metadata: labels: apps.kommander.mesosphere.io/federated-config-map-name: external-dns-overrides name: external-dns-config-overrides namespace: ${WORKSPACE_NAMESPACE} ownerReferences: - apiVersion: apps.kommander.d2iq.io/v1alpha2 kind: AppDeployment name: external-dns uid: ${EXTERNAL_DNS_UID} data: values.yaml: | aws: region: "${EXTERNAL_DNS_AWS_REGION}" assumeRoleArn: "${EXTERNAL_DNS_AWS_ASSUME_ROLE}" preferCNAME: true policy: upsert-only txtPrefix: local- domainFilters: - "${EXTERNAL_DNS_DOMAIN_FILTER}" EOF ```