DevOpsTerraformEKS09. Lets Encrypt 인증서 2

09. Lets Encrypt 인증서 2

Cert manager

Cert manager를 우선 설치해야하는데, 최신 버전은 여기서 확인할 수 있다.
최신 버전 : https://github.com/cert-manager/cert-manager/releases

CLI로 최신버전 확인하기

curl -s https://api.github.com/repos/cert-manager/cert-manager/releases/latest | grep tag_name

버전 확인 후 다운로드 받기

curl -L -o cert-manager.yaml https://github.com/cert-manager/cert-manager/releases/download/v1.18.2/cert-manager.yaml

위처럼 yaml 파일을 다운로드 받고 kubectl apply -f를 할 수도 있지만 여기서는 Terraform을 이용해서 설치하겠다. Terraform으로 위와 동등한 방식은

4-cert-manager.tf생성

resource "helm_release" "cert_manager" {
  name = "cert-manager"
 
  repository       = "https://charts.jetstack.io"
  chart            = "cert-manager"
  namespace        = "cert-manager"
  create_namespace = true
  version          = "v1.18.2"
 
  set {
    name  = "installCRDs"
    value = "true"
  }
 
  # Used for the DNS-01 challenge.
  set {
    name  = "serviceAccount.name"
    value = "cert-manager"
  }
 
  # Used for the DNS-01 challenge.
  set {
    name  = "serviceAccount.annotations.eks\\.amazonaws\\.com/role-arn"
    value = aws_iam_role.dns_manager.arn
  }
}

HTTP-01 챌린지를 위해서는 line 14까지만 필요하지만 DNS-01을 위해서는 위 주석내용에 썼듯이 추가적인 SA(Service Account)와 IAM role 설정이 더 필요하다.

5-irsa.tf

# Used for the DNS-01 challenge.
data "tls_certificate" "eks" {
  url = data.aws_eks_cluster.cluster.identity[0].oidc[0].issuer
}
 
data "aws_iam_openid_connect_provider" "eks" {
  url = data.aws_eks_cluster.cluster.identity[0].oidc[0].issuer
}

6-dns-manager.tf
DNS-01 챌린지는 Route53에 임시로 TXT record을 생성해야하기 때문에, 앞에 설정한 OIDC를 이용해 AWS Route53에 다음과 같이 IAM permission을 관리한다.

# Used for the DNS-01 challenge.
 
data "aws_iam_policy_document" "dns_manager" {
  statement {
    actions = ["sts:AssumeRoleWithWebIdentity"]
    effect  = "Allow"
 
    condition {
      test     = "StringEquals"
      variable = "${replace(data.aws_iam_openid_connect_provider.eks.url, "https://", "")}:sub"
      values   = ["system:serviceaccount:cert-manager:cert-manager"]
    }
 
    principals {
      identifiers = [data.aws_iam_openid_connect_provider.eks.arn]
      type        = "Federated"
    }
  }
}
 
resource "aws_iam_role" "dns_manager" {
  assume_role_policy = data.aws_iam_policy_document.dns_manager.json
  name               = "${var.env}-${var.eks_cluster_name}-dns-manager"
}
 
resource "aws_iam_policy" "dns_manager" {
  name = "dns_manager"
  path = "/"
 
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = [
          "route53:GetChange",
        ]
        Effect   = "Allow"
        Resource = "arn:aws:route53:::change/*"
      },
      {
        Action = [
          "route53:ChangeResourceRecordSets",
          "route53:ListResourceRecordSets"
        ]
        Effect   = "Allow"
        Resource = "arn:aws:route53:::hostedzone/*"
      },
      {
        Action = [
          "route53:ListHostedZonesByName",
          "route53:ListHostedZones"
        ]
        Effect   = "Allow"
        Resource = "*"
      },
    ]
  })
}
 
resource "aws_iam_role_policy_attachment" "dns_manager" {
  policy_arn = aws_iam_policy.dns_manager.arn
  role       = aws_iam_role.dns_manager.name
}

아래 두 개 파일은 .tf파일이 아니고 .yaml 파일이다.

Issuer 혹은 ClusterIssuer 를 생성하는 방법이 있는데, ClusterIssuer가 한 번만 생성하면 모든 namespace에서 사용할 수 있으므로 더 유동적이다.

issuer-production.yaml생성

---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: production-cluster-issuer
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: production-cluster-issuer
    solvers:
      - dns01:
          route53:
            {}
        selector:
          dnsZones:
            - "plateer.io"

certificate.yaml

---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: api-cert
  namespace: istio-ingress
spec:
  secretName: app-cert-secret
  dnsNames:
    - "*.plateer.io"
  issuerRef:
    name: production-cluster-issuer
    kind: ClusterIssuer
    group: cert-manager.io
  privateKey:
    rotationPolicy: Always

위 두 파일을 생성하고 둘 다 적용한다.

kubectl apply -f issuer-production.yaml
kubectl apply -f certificate.yaml

위에 Terraform으로 cert-manager를 설치하고도 적용까지 시간이 조금 걸리고 약 12분
그리고 ACME challenge도 약 2
3분 정도 시간이 걸린다.
그러므로 위 명령어로 실행시 에러가 나오면 조금 기다렸다가 다시 실행해본다.

Staging 과 Production Issuer의 차이

Staging IssuerProduction Issuer
URL: https://acme-staging-v02.api.letsencrypt.org/directoryURL: https://acme-v02.api.letsencrypt.org/directory
Rate Limits: Very high (for testing)Rate Limits: Strict (20 certs/week per domain)
Certificates: Not trusted by browsersCertificates: Trusted by all browsers
Purpose: Testing and developmentPurpose: Production use
Production은 일주일에 20번 발급이 한도다. (충분하지만 무한 테스팅은 불가능)

최종 확인

kubectl get Certificate -A

를 해서 Certificate의 READY 상태가 true 이면 챌린지를 통과하여 발급이 된 상태다.
READY state가 3분 넘게 계속 false라면

kubectl describe CertificateRequest
kubectl describe Order   # Challenge가 생성되었는지 여부
kubectl describe Challenge    # 만약 challege가 실패했다면 왜 실패했는지 보여준다.

로 troubleshoot한다.