Skip to content

Security

TLS

Use the following Ingress Annotations to 1. expose your services port, 2. get a route53 record, 3. acquire a TLS cert:

Info

cert-manager only watches networking.k8s.io/v1 Ingress resources so do not use Trafiks IngressRule CR.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: host
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt
    cert-manager.io/acme-challenge-type: dns01
    traefik.ingress.kubernetes.io/router.entrypoints: websecure
    traefik.ingress.kubernetes.io/router.tls: "true"
    external-dns.alpha.kubernetes.io/hostname: host.dev.clearroute.io
spec:
  ingressClassName: traefik
  rules:
    - host: host.sandbox.clearroute.io
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: host
                port:
                  number: 80
  tls:
    - hosts: [host.sandbox.clearroute.io]
      secretName: host-cert

IAM

IRSA

If your Kubernetes Application Service Account needs to assume an IAM role you can leverage IRSA.

Here are to example to create an IAM role & policy and map it to the specified namespace & service_account_name. Once created you can annotate your ServiceAccount with the role_arn (see example below)

1. Create IAM role & policy and map it to a namespace + ServiceAccount:

# infra/cluster/<cluster_name>.tfvars
irsa = {
  cluster_autoscaler = {
    namespace            = "kube-system"
    service_account_name = "cluster-autoscaler"
    iam_policy = {
      # # https://docs.aws.amazon.com/eks/latest/best-practices/cas.html
      "ClusterAutoscalerAll" = {
        actions = [
          "autoscaling:DescribeAutoScalingGroups",
          "autoscaling:DescribeAutoScalingInstances",
          "autoscaling:DescribeLaunchConfigurations",
          "autoscaling:DescribeTags",
          "autoscaling:SetDesiredCapacity",
          "autoscaling:TerminateInstanceInAutoScalingGroup",
          "ec2:DescribeLaunchTemplateVersions",
          "ec2:DescribeInstanceTypes",
          "eks:DescribeNodegroup"
        ]
        resources = ["*"]
      }
    }
  }
  external_secrets_operator = {
    namespace            = "external-secrets"
    service_account_name = "external-secrets"
    iam_policy = {
      "ExternalSecretsOperatorAll" = {
        actions = [
          "secretsmanager:ListSecrets",
          "secretsmanager:BatchGetSecretValue",
          "secretsmanager:GetResourcePolicy",
          "secretsmanager:GetSecretValue",
          "secretsmanager:DescribeSecret",
          "secretsmanager:ListSecretVersionIds"
        ]
        resources = ["*"]
      }
    }
  }
}

2. Annotate your applications ServiceAccount

That steps depends on your application annotation

# applications/{external,internal}/<app-name>/overlays/{sandbox,dev,prod}/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

[...]

patches:
  - target:
      kind: ServiceAccount
      name: serviceaccount
    patch: |
      - op: replace
        path: /metadata/annotations/eks.amazonaws.com~1role-arn # kustomize uses ~1 to escape / in json pointer paths
        value: <role_arn> # e.g arn:aws:iam::799468650620:role/clearcomply-backend-dev

IRSA - Cross Account

Similarly, if your applications ServiceAccount needs to assume a role in ClearRoutes main account (e.g for Route53 operations), you must use the irsa_clerroute_account variable:

Example:

irsa_clearroute_account = {
  external_dns = {
    namespace            = "external-dns"
    service_account_name = "external-dns"
    iam_policy = {
      # https://kubernetes-sigs.github.io/external-dns/latest/docs/tutorials/aws/#iam-policy
      "ChangeResourceRecordSets" = {
        actions   = ["route53:ChangeResourceRecordSets"]
        resources = ["arn:aws:route53:::hostedzone/*"]
      }
      "Route53Records" = {
        actions = [
          "route53:ListHostedZones",
          "route53:ListResourceRecordSets",
          "route53:ListTagsForResources"
        ]
        resources = ["*"]
      }
    }
  }
  cert_manager = {
    namespace            = "cert-manager"
    service_account_name = "cert-manager"
    iam_policy = {
      "ChangeResourceRecordSets" = {
        actions = [
          "route53:ChangeResourceRecordSets",
          "route53:ListResourceRecordSets"
        ]
        resources = ["arn:aws:route53:::hostedzone/*"]
        conditions = {
          "ForAllValues:StringEquals" = {
            variable = "route53:ChangeResourceRecordSetsRecordTypes"
            values   = ["TXT"]
          }
        }
      }
      "ChangeResourceRecordSets" = {
        actions = [
          "route53:ListHostedZones",
          "route53:ListHostedZonesByName",
          "route53:GetChange",
          "route53:GetHostedZone",
        ]
        resources = ["*"]
      }
    }
  }
}

Secrets Management

Synchronizing Secrets from AWS SSM

Here is an example for how to create (and synchronize) a Kubernetes Secret from an AWS Secrets Manager Secret:

apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
  name: <name>
  namespace: default
spec:
  refreshInterval: 1h
  secretStoreRef:
    kind: ClusterSecretStore
    name: aws-secretsmanager
  target:
    name: <secret-name> # the kubernetes secret name
  data:
    - secretKey: username # the kubernetes secret key
      remoteRef:
        key: constellation/external-secrets/test # change to your path
        property: username # the AWS secret key
    - secretKey: password
      remoteRef:
        key: constellation/external-secrets/test
        property: password 

if you instead just want to map every key-value pair from your AWS secret you can use dataFrom (this method is preferred):

apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
  name: <name>
  namespace: default
spec:
  refreshInterval: 1h
  secretStoreRef:
    kind: ClusterSecretStore
    name: aws-secretsmanager
  target:
    name: <secret-name> # the kubernetes secret name
  dataFrom:
   - extract:
      key: constellation/external-secrets/test

You can then mount and consume the secret in your Pods using env or envFrom.

ImagePullSecrets

If you need to pull from a private registry, you need to create an ImagePullSecret, here is an example:

apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
  name: clearroute-image-pull-secret
  annotations:
    argocd.argoproj.io/hook: PreSync
spec:
  refreshInterval: 1h
  secretStoreRef:
    kind: ClusterSecretStore
    name: aws-secretsmanager
  target:
    template:
      type: kubernetes.io/dockerconfigjson
      data:
        .dockerconfigjson: |
          {
            "auths": {
              "ghcr.io": {
                "username": "{{ .username }}",
                "password": "{{ .password }}",
                "auth": "{{ printf "%s:%s" .username .password | b64enc }}"
              }
            }
          }  
  dataFrom:
    - extract:
        key: constellation/sandbox/clearroute-pullsecret # contains username & password