---
# System prepended metadata

title: Configuring Let's Encrypt certificate on attached clusters

---

# 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
```
