Envoy Gateway を使って argocd-server に HTTPS でアクセスする

Table of Contents

1. はじめに

今回は、自宅の Kubernetes クラスターで、Kubernetes の Gateway API 実装の一つである Envoy Gateway を使って、argocd-server に HTTPS でアクセスできるようにするまでの手順を紹介します。

Argo CD のドキュメントには Ingress Configuration のページはあるのですが、Gateway API を使った設定方法は見当たらなかったため、色々と試行錯誤しながらやってみました。

2. Argo CDについて

まずは Argo CD について簡単に紹介します。

Argo CD は、Kubernetes クラスター上で GitOps を実践するための CD (Continuous Delivery) ツールです。 Git リポジトリを SSOT (Single Source of Truth) として、宣言的かつ自動的な Kubernetes リソースの管理ができます。

Argo CDのアーキテクチャ From https://argo-cd.readthedocs.io/en/stable/#architecture

例えば以下のような画面で登録した Application の状態を確認したり、Git リポジトリとの同期操作を行ったりできます。

Argo CD Web UI From https://argo-cd.readthedocs.io/en/stable/#what-is-argo-cd

Argo CD は複数のコンポーネントで構成されており、今回 HTTPS でアクセスできるようにする argocd-server は、先ほど見せた Web UI と API エンドポイントを提供するコンポーネントです。

今回はこの argocd-server に、Envoy Gateway を通して HTTPS でセキュアにアクセスできるようにすることを目指してやっていきます。

3. Envoy Gatewayについて

Envoy Gateway は、Envoy Proxy を利用した Kubernetes の Gateway API 実装の一つです。

Envoy Gateway Architecture From https://gateway.envoyproxy.io/docs/

Gateway API は、Kubernetes ネイティブな方法でロードバランシングやトラフィック管理を行うための標準的な API 仕様で、従来の Ingress リソースに比べてより柔軟で拡張性の高い設計をすることができます。 基本的に新規で作成するなら Gateway API を使うのがいい(はず?)です。

Envoy Gateway を使うことで Kubernetes クラスター内外のトラフィックを制御することができ、クラスタ外から Service にアクセスするためのロードバランサとして機能させることもできます。 Gateway API は当然 Kubernetes リソースのため、それらの制御はすべて YAML マニフェストだけで行うことができるのも嬉しいポイントです。

4. 環境構成

今回の環境はこんな感じです。

  • Kubernetes クラスター: 自宅サーバー上の環境(kubeadm + OVN-Kubernetes)
  • Envoy Gateway: v1.5.0
  • Argo CD: v3.0.16
  • 証明書: CA/Server 証明書を Ansible で作成して Kubernetes Secret に登録

Kubernetes クラスターについてはこちらの記事で構築方法を紹介していますので、興味があればぜひそちらもご覧ください。

5. インストール手順

5.1 Argo CDのインストール

Argo CD のインストールは基本的に 公式ドキュメント 通りにやりますが、マニフェストの管理は kustomize で行いました。

kustomize/argocd/
├── kustomization.yaml
├── namespace.yaml
├── patch-argocd-cmd-params-cm.yaml
└── http-route.yaml

TLS 終端は Envoy Gateway で行うため、patch-argocd-cmd-params-cm.yaml では argocd-server が insecure モードで起動して HTTP だけリッスンするように設定します。

apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cmd-params-cm
data:
  server.insecure: "true"

こちらの設定についても 公式ドキュメント に説明があり、server.xxx の形式で ConfigMap に書いたものが argocd-server の起動オプションとして渡されます。

The commands can also be configured by setting the respective flag of the available options in argocd-cmd-params-cm.yaml. Each component has a specific prefix associated with it.

argocd-server                 --> server
argocd-repo-server            --> reposerver
argocd-application-controller --> controller

基本的に Argo CD 自体のインストールは以上ですが、Gateway API の HTTPRoute リソースについては後ほど説明します。

5.2 Envoy Gatewayのインストール

Envoy Gateway 本体のインストールは先ほど作成した Argo CD を利用して行います。

筆者の環境では MetalLB の LoadBalancer が使えたため、

kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'

のようにして一時的に argocd-server の Service にアクセスできるようにして Argo CD を利用しました。

Argo CD による Envoy Gateway のインストール手順は 公式ドキュメント に書いてあるものを参考にしています。

cat <<EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: envoy-gateway
  namespace: argocd
spec:
  project: default
  source:
    chart: gateway-helm
    repoURL: docker.io/envoyproxy
    targetRevision: v1.5.0
  destination:
    namespace: envoy-gateway-system
    server: https://kubernetes.default.svc
  syncPolicy:
    syncOptions:
    - CreateNamespace=true
    - ServerSideApply=true
    automated:
      prune: true
      selfHeal: true
EOF

インストール後、Envoy Gateway のコンポーネントが起動していることを確認します。

$ kubectl -n envoy-gateway-system get pods
NAME                              READY   STATUS    RESTARTS   AGE
envoy-gateway-5d4b5b9f4c-xyz12    1/1     Running   0          2m30s

これで Envoy Gateway 自体のインストールは完了です。 Gateway API のリソース作成は後ほど行います。

5.3 証明書の準備

HTTPS 接続のために Envoy Gateway が使うための CA/Server 証明書を Ansible で作成します。

CA 証明書の作成はこんな感じです。

# roles/cert/tasks/ca.yml

- name: Generate CA private key
  community.crypto.openssl_privatekey:
    path: "{{ cert.ca.key_path }}"
    type: RSA
    size: 4096
    owner: root
    group: root
    mode: "0600"

- name: Generate CA certificate signing request
  community.crypto.openssl_csr:
    path: "{{ cert.ca.csr_path }}"
    privatekey_path: "{{ cert.ca.key_path }}"
    organization_name: "{{ cert.ca.organization_name }}"
    common_name: "{{ cert.ca.common_name }}"
    basic_constraints:
      - "CA:TRUE"
    basic_constraints_critical: true
    key_usage:
      - keyCertSign
      - cRLSign
    key_usage_critical: true
    owner: root
    group: root
    mode: "0644"

- name: Generate CA certificate
  community.crypto.x509_certificate:
    path: "{{ cert.ca.cert_path }}"
    privatekey_path: "{{ cert.ca.key_path }}"
    csr_path: "{{ cert.ca.csr_path }}"
    provider: selfsigned
    selfsigned_not_after: "+{{ cert.ca.validity_days }}d"
    owner: root
    group: root
    mode: "0644"

- name: Display CA certificate info
  community.crypto.x509_certificate_info:
    path: "{{ cert.ca.cert_path }}"
  register: ca_cert_info

- name: Show CA certificate details
  ansible.builtin.debug:
    msg:
      - "CA Certificate created successfully"
      - "Subject: {{ ca_cert_info.subject }}"
      - "Valid from: {{ ca_cert_info.not_before }}"
      - "Valid until: {{ ca_cert_info.not_after }}"
      - "Serial number: {{ ca_cert_info.serial_number }}"

必要最低限、common name と organization_name を指定し、鍵の作成 -> CSR の作成 -> CA 証明書の作成の順で実行しています。

Server 証明書もほとんど同じです。

# roles/cert/tasks/server.yml

- name: Generate server private key
  community.crypto.openssl_privatekey:
    path: "{{ cert.server.key_path }}"
    size: 2048
    type: RSA
    owner: root
    group: root
    mode: "0600"

- name: Generate server certificate signing request
  community.crypto.openssl_csr:
    path: "{{ cert.server.csr_path }}"
    privatekey_path: "{{ cert.server.key_path }}"
    organization_name: "{{ cert.server.organization_name }}"
    common_name: "{{ cert.server.common_name }}"
    key_usage:
      - digitalSignature
      - keyEncipherment
    extended_key_usage:
      - serverAuth
      - clientAuth
    owner: root
    group: root
    mode: "0644"

- name: Generate server certificate signed by CA
  community.crypto.x509_certificate:
    path: "{{ cert.server.cert_path }}"
    privatekey_path: "{{ cert.server.key_path }}"
    csr_path: "{{ cert.server.csr_path }}"
    provider: ownca
    ownca_path: "{{ cert.ca.cert_path }}"
    ownca_privatekey_path: "{{ cert.ca.key_path }}"
    ownca_not_after: "+{{ cert.server.validity_days }}d"
    owner: root
    group: root
    mode: "0644"

- name: Display server certificate info
  community.crypto.x509_certificate_info:
    path: "{{ cert.server.cert_path }}"
  register: server_cert_info

- name: Show server certificate details
  ansible.builtin.debug:
    msg:
      - "Server Certificate created successfully"
      - "Subject: {{ server_cert_info.subject }}"
      - "Valid from: {{ server_cert_info.not_before }}"
      - "Valid until: {{ server_cert_info.not_after }}"
      - "Serial number: {{ server_cert_info.serial_number }}"
      - "Subject Alt Names: {{ server_cert_info.subject_alt_name | default(['None']) }}"

- name: Verify server certificate against CA
  community.crypto.x509_certificate_info:
    path: "{{ cert.server.cert_path }}"
    valid_at:
      today: "+0d"
  register: server_cert_verification

- name: Show certificate verification result
  ansible.builtin.debug:
    msg: "Server certificate is {{ 'valid' if server_cert_verification.valid_at.today else 'invalid' }}"

署名には先ほど作成した CA 証明書を使っています。

このように作成した Server 証明書と鍵を、Kubernetes Secret に登録します。

- name: Read server certificate
  ansible.builtin.slurp:
    src: "{{ cert.server.cert_path }}"
  register: server_cert_content

- name: Read server private key
  ansible.builtin.slurp:
    src: "{{ cert.server.key_path }}"
  register: server_key_content

- name: Create TLS secret in Kubernetes
  kubernetes.core.k8s:
    api_version: v1
    kind: Secret
    name: "{{ k8s.secret_name }}"
    namespace: "{{ k8s.namespace }}"
    definition:
      type: kubernetes.io/tls
      data:
        tls.crt: "{{ server_cert_content.content }}"
        tls.key: "{{ server_key_content.content }}"

TLS secrets の書き方については Kubernetes のドキュメント に記載があります。

これらの Ansible Playbook を実行することで、Envoy Gateway 用の Server 証明書が Kubernetes Secret に登録されます。

$ kubectl get secret -n gateway-system
NAME                    TYPE                DATA   AGE
cert-home-tomokon-net   kubernetes.io/tls   2      2m

もしかしたらこういうのって cert-manager とかを使った方が楽だったかもしれませんが、終わってから気づきました…

何はともあれ、作成した Server 証明書を信頼できるものと定義するためには、クライアント側(今回は macOS)に CA 証明書をインポートする必要があります。

こんな感じで CA 証明書として登録されており、“Trusted” になっていれば OK です。

CA証明書の登録
CA証明書のTrust設定

ここまでで、Gateway API のリソースを作成する準備が整いました。 ここからは実際に Gateway API リソースの作成に入っていきます。

6. Gateway設定

6.1 GatewayClassの作成

GatewayClass は Gateway API におけるコントローラーの定義のクラスタリソースです。 どのコントローラー(今回は Envoy Gateway)が Gateway を管理するかを指定します。

IngressClass みたいなものだと思ってもらえれば大丈夫です。

# kustomize/gateway/gateway-class.yaml

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: envoy
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller

GatewayClass はクラスター全体で共有されるリソースのため、namespace 指定は不要です。

6.2 Gatewayの作成

続いて先ほどの GatewayClass を使用して Gateway リソースを作成していきます。

Gateway は実際のロードバランサーを作成するためのリソースです。

今回はHTTPS(443ポート)で listen し、TLS 終端を行います。 また、特定のラベルがついた他の Namespace に存在する HTTPRoute リソースから参照できるように allowedRoutes の設定をしています:

# kustomize/gateway/gateway.yaml

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: envoy-http
  namespace: gateway-system
spec:
  gatewayClassName: envoy
  listeners:
    - name: https
      protocol: HTTPS
      port: 443
      hostname: "*.home.tomokon.net"
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              shared-gateway-access: "true"
      tls:
        mode: Terminate
        certificateRefs:
          - kind: Secret
            group: ""
            name: cert-home-tomokon-net

tls のフィールドでは先ほど作成した Server 証明書を含む Secret を指定しています。 また、hostname: "*.home.tomokon.net" のようにワイルドカードで指定することで、home.tomokon.net のサブドメインを指定した xxxRoute リソースから紐付けられるようになります。

Gateway を作成すると、Envoy Gateway によって type: LoadBalancer の Service が自動的に作成されるようです:

$ kubectl get svc -n envoy-gateway-system | hgrep -i loadbalancer
NAME                                       TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)                                            AGE
envoy-gateway-system-envoy-http-165ae7c4   LoadBalancer   10.96.181.180   192.168.1.151   443:30384/TCP                                      2d8h

6.3 HTTPRouteの作成

いよいよ HTTPRoute リソースの作成です。

HTTPRoute は特定のホスト名とパスを、バックエンドの Service にルーティングすることができます。 これによって argocd-server に指定したホスト名で、かつ HTTPS でアクセスできるようにします:

# kustomize/argocd/http-route.yaml

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: argocd-server
  namespace: argocd
spec:
  parentRefs:
    - name: envoy-http
      namespace: gateway-system
  hostnames:
    - "argocd.home.tomokon.net"
  rules:
    - backendRefs:
        - name: argocd-server
          port: 80

parentRefshostnames を見て自動的に先ほど作成した Gateway リソースと紐づけられますが、そのためには Gateway の allowedRoutes で指定した条件を満たすために argocd Namespace に特定のラベルをつける必要があります:

apiVersion: v1
kind: Namespace
metadata:
  name: argocd
  labels:
    name: argocd
    shared-gateway-access: "true"

これで、argocd namespace の HTTPRoute が gateway-system の Gateway に紐付けられるようになります。

また、作成した HTTPRoute は argocd.home.tomokon.net のホスト名でアクセスできるようにしているため、先ほど Gateway リソース作成時に払い出された Service type=LoadBalancer の EXTERNAL-IP に対して、DNS で argocd.home.tomokon.net を解決できるように設定しておく必要があります。

筆者の環境では自宅サーバー上の DNS サーバーの BIND9 にレコードを追加しました。 DNS サーバーの構築については こちらの記事 で紹介していますので、興味があればぜひそちらもご覧ください。

$ dig argocd.home.tomokon.net +short
gateway.home.tomokon.net.
192.168.1.151

7. 動作確認

設定が完了したら、HTTPS でアクセスできることを確認します。

$ curl -I https://argocd.home.tomokon.net
HTTP/2 200
accept-ranges: bytes
content-length: 788
content-security-policy: frame-ancestors 'self';
content-type: text/html; charset=utf-8
vary: Accept-Encoding
x-frame-options: sameorigin
x-xss-protection: 1
date: Tue, 09 Sep 2025 14:17:19 GMT

ブラウザからも正常に Argo CD の Web UI にアクセスできることを確認しました。

自宅のArgoCDブラウザ画面

8. まとめ

今回は自宅の Kubernetes クラスターで Envoy Gateway を使って Argo CD サーバーに HTTPS でアクセスする環境を構築しました!

Envoy Gateway は Gateway API の理解さえできれば割と素直に扱えて使いやすかったです。 今後も色んなアプリケーションをこの Gateway を通してアクセスできるようにしていきたいと思います。

最後まで読んでいただき、ありがとうございました!

9. 参考資料

  1. Envoy Gateway Documentation
  2. Gateway API
  3. Argo CD - Getting Started
  4. Argo CD - Ingress Configuration
  5. Argo CD - Server Commands
  6. Envoy Gateway - Installation with Argo CD
  7. Kubernetes Documentation - TLS Secrets
  8. Gateway API - GatewayClass
  9. Gateway API - Gateway
  10. Gateway API - HTTPRoute