😊

Cilium Gatewayと連動するcert-managerの導入でTLS証明書自動化

に公開

https://cert-manager.io/

Cert-managerはTLS証明書自動管理に役立つツールで、KubernetesクラスタでのHTTPSウェブアクセスの用意が簡単にできるようになります。

先のポストではCilium Gatewayを構築し、whoamiサービスへのplain httpのアクセスを用意するところまでカバーしましたが、今回はcert-managerをセットアップしてhttpsアクセスを用意します。

ひとつ前のポスト:おうちKubernetesで構築するCilium Gateway

なお、しばらく前にCiliumではなくCalico, MetalLB, NGINX Gateway Fabricの組み合わせでcert-managerをセットアップしたときのポストもあるので興味がある方はご覧ください。

post on the previous cert-manager setup with calico & ngf

前提

  • fluxcdとGitLabでGitOpsがセットアップ済み
  • 次の5つのflux kustomizationsがセットアップ済み
    • ./clusters/lab-hlv3 as flux-system created during bootstrap process
    • ./infrastructure/lab-hlv3/controllers as infra-controllers
    • ./infrastructure/lab-hlv3/configs as infra-configs
    • ./apps/lab-hlv3 as apps
    • ./sops/lab-hlv3 as sops
  • ひとつ前のポストでカバーしたCilium Gatewayのセットアップ
    • Gateway API v1.2.0のインストール
    • Gateway作成、IPアドレス割り当て(本記事ではダミーIPアドレス192.0.2.83)、L2Announcement
    • traefik/whoamiのdeployment, service, およびserviceとgatewayのhttp listenerを繋ぐhttprouteの作成

手順

  • On infra-controllers
    • Cert-managerをhelmでインストール
  • On infra-configs
    • Issuer作成
    • Issuerと連動するようにGatewayコンフィグ更新
    • Gatewayにhttps listener追加
    • https listenerとwhoamiサービスを繋ぐHTTPRouteの作成

Cert-managerのhelmチャート

Cert-managerのhelmチャートが公開されているjetstack helmレポジトリを追加します。

# add helm repository
helm repo add jetstack https://charts.jetstack.io

レポジトリの中はこのような感じです。

# run helm repo update to update repository information

$ helm search repo jetstack
NAME                                    CHART VERSION   APP VERSION     DESCRIPTION
jetstack/cert-manager                   v1.17.1         v1.17.1         A Helm chart for cert-manager
jetstack/cert-manager-approver-policy   v0.19.0         v0.19.0         approver-policy is a CertificateRequest approve...
jetstack/cert-manager-csi-driver        v0.10.2         v0.10.2         cert-manager csi-driver enables issuing secretl...
jetstack/cert-manager-csi-driver-spiffe v0.8.2          v0.8.2          csi-driver-spiffe is a Kubernetes CSI plugin wh...
jetstack/cert-manager-google-cas-issuer v0.9.0          v0.9.0          A Helm chart for jetstack/google-cas-issuer
jetstack/cert-manager-istio-csr         v0.14.0         v0.14.0         istio-csr enables the use of cert-manager for i...
jetstack/cert-manager-trust             v0.2.1          v0.2.0          DEPRECATED: The old name for trust-manager. Use...
jetstack/finops-dashboards              v0.0.5          0.0.5           A Helm chart for Kubernetes
jetstack/finops-policies                v0.0.6          v0.0.6          A Helm chart for Kubernetes
jetstack/finops-stack                   v0.0.5          0.0.3           A FinOps Stack for Kubernetes
jetstack/trust-manager                  v0.16.0         v0.16.0         trust-manager is the easiest way to manage TLS ...
jetstack/version-checker                v0.8.6          v0.8.6          A Helm chart for version-checker

valuesファイル

helm showコマンドを使って指定のバージョンのcert-manager helmチャートのvaluesファイルをダウンロードします。infra-controllers flux kustomizationよりインストールするので、そのあたりにディレクトリを用意してダウンロードしたファイルを格納しておきます。

いつでもダウンロードしなおせるファイルではありますが、私は編集前後のvaluesファイル両方ともGitOpsレポジトリに置いておきたいので以下のような操作になっています。

# on gitops repo
cd infrastructure/lab-hlv3/controllers/default-values
helm show values jetstack/cert-manager --version v1.17.1 > cert-manager-v1.17.1-values.yaml
cp cert-manager-v1.17.1-values.yaml ../values/cert-manager-values.yaml

# and edit values file at ./infrastructure/lab-hlv3/controllers/values/cert-manager-values.yaml

v1.17.1のcert-manager helmチャートのvaluesファイルへの変更はこのようになりました。

  • cert-managerのCRDsインストール
    • crds.enabled: true
  • Gateway APIのサポートを有効に
    • config.apiVersion: controller.config.cert-manager.io/v1alpha1
    • config.kind: ControllerConfiguration
    • config.enableGatewayAPI: true
  • DNS01チャレンジ時の確認に利用するDNSサーバの指定
    • dns01RecursiveNameservers: "1.1.1.1:53,1.0.0.1:53"
    • dns01RecursiveNameserversOnly: true

fluxのhelm repoとhelm releaseマニフェストを用意するスクリプト

https://fluxcd.io/flux/guides/helmreleases/

fluxcdのGitOpsでhelmで何かをインストールする場合、手順としては (1) HelmRepositoryを追加し、(2) そのレポジトリのチャートを指定したHelmReleaseを追加することで、fluxシステムが必要なものをプルし、インストールします。

以下はfluxcdのCRDsですが、これらのマニフェストを生成するためのfluxコマンドが用意されています(flux create source helmflux create helmreleaseなど)。

$ kubectl api-resources | grep fluxcd
helmreleases                        hr                                  helm.toolkit.fluxcd.io/v2                true         HelmRelease
kustomizations                      ks                                  kustomize.toolkit.fluxcd.io/v1           true         Kustomization
alerts                                                                  notification.toolkit.fluxcd.io/v1beta3   true         Alert
providers                                                               notification.toolkit.fluxcd.io/v1beta3   true         Provider
receivers                                                               notification.toolkit.fluxcd.io/v1        true         Receiver
buckets                                                                 source.toolkit.fluxcd.io/v1              true         Bucket
gitrepositories                     gitrepo                             source.toolkit.fluxcd.io/v1              true         GitRepository
helmcharts                          hc                                  source.toolkit.fluxcd.io/v1              true         HelmChart
helmrepositories                    helmrepo                            source.toolkit.fluxcd.io/v1              true         HelmRepository
ocirepositories                     ocirepo                             source.toolkit.fluxcd.io/v1beta2         true         OCIRepository

$ flux create source helm --help
The create source helm command generates a HelmRepository resource and waits for it to fetch the index.
For private Helm repositories, the basic authentication credentials are stored in a Kubernetes secret.

Usage:
  flux create source helm [name] [flags]

Examples:
  # Create a source for an HTTPS public Helm repository
  flux create source helm podinfo \
    --url=https://stefanprodan.github.io/podinfo \
    --interval=10m

$ flux create helmrelease --help
The helmrelease create command generates a HelmRelease resource for a given HelmRepository source.

Usage:
  flux create helmrelease [name] [flags]

Aliases:
  helmrelease, hr

Examples:
  # Create a HelmRelease with a chart from a HelmRepository source
  flux create hr podinfo \
    --interval=10m \
    --source=HelmRepository/podinfo \
    --chart=podinfo \
    --chart-version=">4.0.0"

上は--help出力の一部ですが、コマンド例をいくつか見る限り割と長いです。

チャートのバージョンなりvaluesファイルでパラメータを変更するとなると、その度にflux createコマンドでマニフェストを作り直す必要があります。スクリプトを用意しておき、その手間を省けるようにしておきます。

以下がいつも私が用意しているHelmRepositoryおよびHelmRelease生成用スクリプトのパターンです。二つのマニフェストを./infrastructure/lab-hlv3/controllers/cert-manager.yamlという一つのファイルに出力します。infra-controllers flux kustomizationに含めるのはこのファイル一つだけとなります。Valuesファイルの中身を更新した時はスクリプトを走らせてcert-manager.yamlファイルを再生成するだけ、そしてバージョンを変更するときはスクリプト内のHelmReleaseより、--chart-versionの値を変更してスクリプトを走らせるだけで済みます。

# on gitops repo
mkdir infrastructure/lab-hlv3/controllers/scripts
cd infrastructure/lab-hlv3/controllers/scripts

# script to generate a file containing flux helmrepo and hr
cat <<'EOF' >cert-manager.sh
#!/bin/bash

# add flux helmrepo to the manifest
flux create source helm cert-manager \
  --url=https://charts.jetstack.io \
  --interval=1h0m0s \
  --export >../cert-manager.yaml

# add flux helm release to the manifest including the customized values.yaml file
flux create helmrelease cert-manager \
  --interval=10m \
  --target-namespace=cert-manager \
  --source=HelmRepository/cert-manager \
  --chart=cert-manager \
  --chart-version=v1.17.1 \
  --values=../values/cert-manager-values.yaml \
  --export >>../cert-manager.yaml
EOF


# run the script to generate the file
chmod u+x cert-manager.sh
./cert-manager.sh

cert-manager namespace

上で用意したHelmReleaseですが、ターゲットnamespaceとして"cert-manager"を指定しています。--create-target-namespaceオプションをセットしていないので、namespaceは別途作っておく必要があります。

Namespaceの内容なりラベルなりをいじりたいといった時に、分けておけばHelmReleaseにはノータッチで作業できるのでこういう形にしています。

# ./clusters/lab-hlv3/namespaces/cert-manager.yaml
---
kind: Namespace
apiVersion: v1
metadata:
  name: cert-manager
  labels:
    service: cert-manager
    type: infrastructure

infra-controllers kustomization

最後に、fluxのinfra-controllers kustomizationを更新し、cert-managerのHelmRepositoryおよびHelmReleaseが含まれているcert-manager.yamlファイルを追加します。

# ./infrastructure/lab-hlv3/controllers/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - cm-placeholder.yaml
  ### crds
  # gateway api
  # version 1.2.0
  - crds/gateway-api/standard/standard-install-v1.2.0.yaml
  - crds/gateway-api/experimental/gateway.networking.k8s.io_tlsroutes-v1.2.0.yaml
  ### infra-controllers
  - cert-manager.yaml

cert-managerインストール後の状態

インストール結果は以下のような感じで確認できると思います。

$ flux get all
NAME                            REVISION                SUSPENDED       READY   MESSAGE
gitrepository/flux-system       main@sha1:5883ff2a      False           True    stored artifact for revision 'main@sha1:5883ff2a'

NAME                            REVISION        SUSPENDED       READY   MESSAGE
helmrepository/cert-manager     sha256:f3211071 False           True    stored artifact: revision 'sha256:f3211071'

NAME                                    REVISION        SUSPENDED       READY   MESSAGE
helmchart/flux-system-cert-manager      v1.17.1         False           True    pulled 'cert-manager' chart with version 'v1.17.1'

NAME                            REVISION        SUSPENDED       READY   MESSAGE
helmrelease/cert-manager        v1.17.1         False           True    Helm install succeeded for release cert-manager/cert-manager-cert-manager.v1 with chart cert-manager@v1.17.1

NAME                            REVISION                SUSPENDED       READY   MESSAGE
kustomization/apps              main@sha1:5883ff2a      False           True    Applied revision: main@sha1:5883ff2a
kustomization/flux-system       main@sha1:5883ff2a      False           True    Applied revision: main@sha1:5883ff2a
kustomization/infra-configs     main@sha1:5883ff2a      False           True    Applied revision: main@sha1:5883ff2a
kustomization/infra-controllers main@sha1:5883ff2a      False           True    Applied revision: main@sha1:5883ff2a


$ kubectl get all -n cert-manager
NAME                                                        READY   STATUS    RESTARTS   AGE
pod/cert-manager-cert-manager-cainjector-86cd99655f-44tn7   1/1     Running   0          2m19s
pod/cert-manager-cert-manager-d56b496b8-ssmmr               1/1     Running   0          2m19s
pod/cert-manager-cert-manager-webhook-54696f8b94-x2jxk      1/1     Running   0          2m19s

NAME                                           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)            AGE
service/cert-manager-cert-manager              ClusterIP   10.96.243.72    <none>        9402/TCP           2m19s
service/cert-manager-cert-manager-cainjector   ClusterIP   10.96.213.160   <none>        9402/TCP           2m20s
service/cert-manager-cert-manager-webhook      ClusterIP   10.96.164.115   <none>        443/TCP,9402/TCP   2m19s

NAME                                                   READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/cert-manager-cert-manager              1/1     1            1           2m19s
deployment.apps/cert-manager-cert-manager-cainjector   1/1     1            1           2m19s
deployment.apps/cert-manager-cert-manager-webhook      1/1     1            1           2m19s

NAME                                                              DESIRED   CURRENT   READY   AGE
replicaset.apps/cert-manager-cert-manager-cainjector-86cd99655f   1         1         1       2m19s
replicaset.apps/cert-manager-cert-manager-d56b496b8               1         1         1       2m19s
replicaset.apps/cert-manager-cert-manager-webhook-54696f8b94      1         1         1       2m19s


$ kubectl api-resources | grep cert
challenges                                                              acme.cert-manager.io/v1                  true         Challenge
orders                                                                  acme.cert-manager.io/v1                  true         Order
certificaterequests                 cr,crs                              cert-manager.io/v1                       true         CertificateRequest
certificates                        cert,certs                          cert-manager.io/v1                       true         Certificate
clusterissuers                                                          cert-manager.io/v1                       false        ClusterIssuer
issuers                                                                 cert-manager.io/v1                       true         Issuer
certificatesigningrequests          csr                                 certificates.k8s.io/v1                   false        CertificateSigningRequest

cert-manager動作チェックスクリプト

https://cert-manager.io/docs/installation/kubectl/#verify

ここではスキップしますが、動作チェックスクリプトが用意されているので確認しておくと安心です。

Issuerの設定

https://cert-manager.io/docs/configuration/acme/

次にIssuerをセットアップしていきます。

The ACME Issuer type represents a single account registered with the Automated Certificate Management Environment (ACME) Certificate Authority server.

基本的に有効な証明書を取得するために必要なことは次のようなことになります:

  • ACMEシステムにリクエストを提示する
  • リクエストしている対象の名称に関するドメインの正当な所有者であることを証明する

私のドメインは以前Google DomainsからCloudflareへ移管しており、今回はCloudflare DNS01チャレンジで権利を証明するようIssuerをコンフィグします。

Cloudflare DNS01チャレンジ

First obtain the API token with appropriate scope, and then create issuer.

まずはCloudflareよりAPIトーケンを取得し、次にIssuerを作ります。

https://cert-manager.io/docs/configuration/acme/dns01/cloudflare/#api-tokens

https://cert-manager.io/docs/reference/api-docs/#cert-manager.io/v1.Issuer

ドキュメント記載の通り、APIトークンに付与する必要のある権限は次の通りです:

  • permissions
    • zone - dns - edit
    • zone - zone -read
  • zone resources
    • include - all zones

APIトークンはsecretとしてkubernetesクラスタ上に作成し、Issuerはそのsecretを参照するようコンフィグすることになります。Secretのファイルは以下のようなファイルを用意してSOPS用ディレクトリに配置し、暗号化したものをcommit/pushします。

(SOPSセットアップ時の記事)

# ./sops/lab-hlv3/gateway/cloudflare-api-token-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: cloudflare-api-token-secret
  namespace: gateway
type: Opaque
stringData:
  api-token: YOUR_API_TOKEN_HERE

次に、infra-configs用ディレクトリ配下にIssuerのファイルを用意します。apiTokenSecretRefのところで先に作成したsecretを参照しています。

# ./infrastructure/lab-hlv3/configs/cert-manager/issuer.yaml
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: issuer
  namespace: gateway
spec:
  acme:
    email: EMAIL_ADDR
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: issuer-account-key
    solvers:
      - dns01:
          cloudflare:
            email: EMAIL_ADDR
            apiTokenSecretRef:
              name: cloudflare-api-token-secret
              key: api-token
        selector:
          dnsZones:
            - "blink-1x52.net"

最後に、flux infra-configs kustomizationファイルに追加して出来上がりです。

# ./infrastructure/lab-hlv3/configs/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - cm-placeholder.yaml
  # cilium gateway
  - cilium/gateway.yaml
  - cilium/ippools-cilium-gateway.yaml
  - cilium/l2announcement-cilium-gateway.yaml
  # cert-manager
  # - cert-manager/cert-manager-test.yaml
  # cloudflare dns01 issuer
  - cert-manager/issuer.yaml

出来上がったIssuerの情報は以下のような形で確認できると思います。

# kubectl describe issuer issuer -n gateway
Status:
  Acme:
    ......
  Conditions:
    Last Transition Time:  2025-03-31T01:21:13Z
    Message:               The ACME account was registered with the ACME server
    Observed Generation:   1
    Reason:                ACMEAccountRegistered
    Status:                True
    Type:                  Ready
Events:                    <none>

証明書のIssuerを利用するようCilium Gatewayを更新

Cilium Gatewayの設定を更新していきます。

Gatewayにhttp listenerを追加した際は、.spec.listeners[]にhttp listenerを追加するだけでしたが、httpsの場合でも同じです。

以下のwhoami-kube-httpsが今回追加するものです。

  • hostnameはドメイン名も含めた宛先名であり、cert-managerが自動的に用意してくれる証明書はこの名前のものとなる
    • 通信としてはSNIがここの値と合致するものがこのlistenerに処理されることになる
  • portは443
  • protocolはHTTPS
  • .spec.listeners[].tlsはplain httpではなかった新たな項目
    • "terminate"モードはgatewayがTLSオフロードし、後ろのサービスへは複合化されたplain httpが流れる形
    • .spec.listeners[].tls.certificateRefsはcert-managerが最終的に有効な証明書を取得し、その証明書と鍵が格納されるsecretの名称であり、cert-managerを利用する際、既存のsecretを参照させるというものではなく好きに名付けてよい

.metadataにも一つ重要な変更があります。annotationsのcert-manager.io/issuerで指定している名称のIssuerが、このgatewayが連携する相手となります。ひとつ前の手順で作成した"issuer"がここでは指定されています。

# ./infrastructure/lab-hlv3/configs/cilium/gateway.yaml
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: cilium-gateway
  namespace: gateway
  annotations:
    cert-manager.io/issuer: issuer
spec:
  gatewayClassName: cilium
  addresses:
    - type: IPAddress
      value: 192.0.2.83
  listeners:
    - name: whoami-kube-http
      hostname: whoami-kube.lab.blink-1x52.net
      port: 80
      protocol: HTTP
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              gateway: cilium

    - name: whoami-kube-https
      hostname: whoami-kube.lab.blink-1x52.net
      port: 443
      protocol: HTTPS
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              gateway: cilium
      tls:
        mode: Terminate
        certificateRefs:
          - name: tls-whoami-kube
            kind: Secret
            namespace: gateway

何がどうなるのか

GatewayとIssuerが連携するよう更新し、有効なhttps listenerを追加すると、cert-managerが拾って署名された有効な証明書を取得するためのプロセスを回し始めます。

TLS証明書と鍵

最終的な状態を最初に見てみましょう。https listenerで指定したsecretを確認してみると、Kubernetes TLS secretが出来ていることが確認できます。

https://kubernetes.io/docs/concepts/configuration/secret/#tls-secrets

# kubectl get secret tls-whoami-kube -n gateway -o yaml
apiVersion: v1
kind: Secret
type: kubernetes.io/tls
metadata:
  annotations:
    cert-manager.io/alt-names: whoami-kube.lab.blink-1x52.net
    cert-manager.io/certificate-name: tls-whoami-kube
    cert-manager.io/common-name: whoami-kube.lab.blink-1x52.net
    cert-manager.io/ip-sans: ""
    cert-manager.io/issuer-group: cert-manager.io
    cert-manager.io/issuer-kind: Issuer
    cert-manager.io/issuer-name: issuer
    cert-manager.io/uri-sans: ""
  creationTimestamp: "2025-03-31T02:08:36Z"
  labels:
    controller.cert-manager.io/fao: "true"
  name: tls-whoami-kube
  namespace: gateway
  resourceVersion: "3969674"
  uid: ec65b140-027b-4a75-91f5-a80709f36cc7
data:
  tls.key: ...
  tls.crt: ...

証明書取得までのプロセス

プロセスフロー図がcert-managerのドキュメントに載っています。

Gatewayにhttps listenerを追加すると、指定したhostnameの証明書が自動的にできていましたが、大まかには次のようなことが起こっています:

  • "Certificate"が作成される
    • 中身は空っぽといえる状態
  • "CertificateRequest"が作成される
    • "Order"が作成され、"Issuer"と連携し"Challenge"を立ち上げ、コンフィグ内容に従って(今回はCloudflareでのDNS01)チャレンジに対処し、証明書と鍵を取得する
  • TLS証明書と鍵データで、certificateRefsで指定した名前のSecretが作成される
  • "Certificate"のステータスが更新される
Challengeの例

Challengeは処理が終わると消えます。以下は処理中のものをキャプチャできたときの出力です。

Statusを確認してください。このChallengeは"Presented: true"であり、"Processing: true"ですが、"State: pending"となっています。なぜならばDNS01チャレンジのためのDNSレコードの確認待ちだからです。

実はこれはcert-managerのhelm chart内、valuesファイルでdns01RecursiveNameservers設定をする前の試行でキャプチャしたものでした。設定変更前は、私のkubernetesクラスタの場合はおうちラボ環境のDNSサーバに問い合わせする形になっており、"blink-1x52.net."ドメインの名前解決については外部のDNSサーバにまで問い合わせされることはなく、Challengeはこの状態でスタックしていました。

$ kubectl describe challenges -n gateway
Name:         tls-whoami-kube-1-3711042244-1698055298
Namespace:    gateway
Labels:       <none>
Annotations:  <none>
API Version:  acme.cert-manager.io/v1
Kind:         Challenge
Metadata:
  Creation Timestamp:  2025-03-31T01:54:16Z
  Finalizers:
    acme.cert-manager.io/finalizer
  Generation:  1
  Owner References:
    API Version:           acme.cert-manager.io/v1
    Block Owner Deletion:  true
    Controller:            true
    Kind:                  Order
    Name:                  tls-whoami-kube-1-3711042244
    UID:                   b3aee648-c1ab-4827-a9c3-42ef041f5456
  Resource Version:        3962141
  UID:                     5121de47-99a5-4010-86a1-c991a40708f3
Spec:
  Authorization URL:  https://acme-v02.api.letsencrypt.org/acme/authz/xxx/xxx
  Dns Name:           whoami-kube.lab.blink-1x52.net
  Issuer Ref:
    Group:  cert-manager.io
    Kind:   Issuer
    Name:   issuer
  Key: KEY_HERE
  Solver:
    dns01:
      Cloudflare:
        API Token Secret Ref:
          Key:   api-token
          Name:  cloudflare-api-token-secret
        Email:   EMAIL_ADDR
    Selector:
      Dns Zones:
        blink-1x52.net
  Token: TOKEN_HERE
  Type:      DNS-01
  URL: CHALLENGE_URL_HERE
  Wildcard:  false
Status:
  Presented:   true
  Processing:  true
  Reason:      Waiting for DNS-01 challenge propagation: DNS record for "whoami-kube.lab.blink-1x52.net" not yet propagated
  State:       pending
Events:
  Type    Reason     Age   From                     Message
  ----    ------     ----  ----                     -------
  Normal  Started    13m   cert-manager-challenges  Challenge scheduled for processing
  Normal  Presented  13m   cert-manager-challenges  Presented challenge using DNS-01 challenge mechanism

HTTPRouteの作成

次に、追加されたhttps listenerと既存のwhoamiサービスを繋ぐHTTPRouteを用意します。

Plain httpのHTTPRouteとほぼ同一です。唯一の変更点は.spec.parentRefs[].sectionNameで指している先がhttps listenerとなっていることだけです。

---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
  name: whoami-https
  namespace: testbed
spec:
  parentRefs:
    - name: cilium-gateway
      sectionName: whoami-kube-https
      namespace: gateway
  hostnames:
    - "whoami-kube.lab.blink-1x52.net"
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: whoami
          port: 80

結果

以下がcurlコマンドの結果です(IPアドレスはダミーの192.0.2.x)。

#$ curl -v https://whoami-kube.lab.blink-1x52.net
* Host whoami-kube.lab.blink-1x52.net:443 was resolved.
* ...
* Connected to whoami-kube.lab.blink-1x52.net (192.0.2.83) port 443
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: /etc/ssl/certs
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519 / RSASSA-PSS
* ALPN: server did not agree on a protocol. Uses default.
* Server certificate:
*  subject: CN=whoami-kube.lab.blink-1x52.net
*  start date: Mar 31 01:10:01 2025 GMT
*  expire date: Jun 29 01:10:00 2025 GMT
*  subjectAltName: host "whoami-kube.lab.blink-1x52.net" matched cert's "whoami-kube.lab.blink-1x52.net"
*  issuer: C=US; O=Let's Encrypt; CN=R11
*  SSL certificate verify ok.
*   Certificate level 0: Public key type RSA (2048/112 Bits/secBits), signed using sha256WithRSAEncryption
*   Certificate level 1: Public key type RSA (2048/112 Bits/secBits), signed using sha256WithRSAEncryption
*   Certificate level 2: Public key type RSA (4096/152 Bits/secBits), signed using sha256WithRSAEncryption
* using HTTP/1.x
> GET / HTTP/1.1
> Host: whoami-kube.lab.blink-1x52.net
> User-Agent: curl/8.5.0
> Accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
< HTTP/1.1 200 OK
< date: Mon, 31 Mar 2025 05:09:34 GMT
< content-length: 339
< content-type: text/plain; charset=utf-8
< x-envoy-upstream-service-time: 0
< server: envoy
<
Hostname: whoami
IP: 127.0.0.1
IP: ::1
IP: 10.0.3.170
IP: fe80::e834:eff:fe1f:44f3
RemoteAddr: 10.0.3.174:41057
GET / HTTP/1.1
Host: whoami-kube.lab.blink-1x52.net
User-Agent: curl/8.5.0
Accept: */*
X-Envoy-Internal: true
X-Forwarded-For: IPADDR_OF_THE_HOST_EXECUTING_CURL
X-Forwarded-Proto: https
X-Request-Id: d3e749cd-70ac-4e83-ab61-28b132dc9a61

* Connection #0 to host whoami-kube.lab.blink-1x52.net left intact

終わりに

今回はcert-managerをセットアップしてTLS証明書管理自動化環境およびCilium Gateway経由のセキュアなウェブアクセスを用意しました。GitOpsでやることを考えるとdeploymentが一定にでき、運用も単純です。私のおうちラボの規模ではたいしたことないですが、大きなクラスタを運用するとなったらこの自動化環境の恩恵はなかなかのものだと思います。

前回も触れましたが繰り返さずにはいられません。クラスタ内のサービスへのHTTPSアクセスの追加作業がどれだけ簡単なことか。。手順は2つだけです。

  • GatewayへのHTTPS listenerの追加
  • listenerとサービスを繋ぐHTTPRouteの作成

Cert-managerは証明書の更新まで面倒を見てくれますので、あとは念のための定期的な確認・レポートスクリプトでも動かしておけば安心です。

Discussion