Share via


サイドカー コンテナー内で Caddy を使用して自動 HTTPS を有効にする

この記事では、Caddy をリバース プロキシとして機能するコンテナー グループ内のサイドカー コンテナーとして使用して、アプリケーションに対して自動的に管理される HTTPS エンドポイントを提供する方法について説明します。

Caddy は、Go で記述された自動 HTTPS を備えた強力でエンタープライズ対応のオープンソース Web サーバーであり、Nginx の代替手段です。

Caddy は Let's Encrypt と通信して証明書を発行する ACMEv2 API (RFC 8555) をサポートしているため、証明書の自動化が可能です。

この例では、Caddy コンテナーのみがポート 80/TCP と 443/TCP で公開されます。 リバース プロキシの背後にあるアプリケーションはプライベートのままです。 Caddy とアプリケーション間のネットワーク通信は、localhost を介して行われます。

Note

これは、Docker Compose で知られている、コンテナーが名前で参照できる、コンテナー グループ内通信とは対照的です。

この例では、リバース プロキシを構成するために必要な Caddyfile を、Azure Storage アカウントでホストされているファイル共有からマウントします。

Note

運用環境のデプロイでは、ほとんどのユーザーが Caddyfile を caddy に基づくカスタム Docker イメージにベイクしたいと考えます。 これにより、コンテナーにファイルをマウントする必要はありません。

前提条件

  • Azure Cloud Shell で Bash 環境を使用します。 詳細については、「Azure Cloud Shell の Bash のクイックスタート」を参照してください。

  • CLI リファレンス コマンドをローカルで実行する場合、Azure CLI をインストールします。 Windows または macOS で実行している場合は、Docker コンテナーで Azure CLI を実行することを検討してください。 詳細については、「Docker コンテナーで Azure CLI を実行する方法」を参照してください。

    • ローカル インストールを使用する場合は、az login コマンドを使用して Azure CLI にサインインします。 認証プロセスを完了するには、ターミナルに表示される手順に従います。 その他のサインイン オプションについては、Azure CLI でのサインインに関するページを参照してください。

    • 初回使用時にインストールを求められたら、Azure CLI 拡張機能をインストールします。 拡張機能の詳細については、Azure CLI で拡張機能を使用する方法に関するページを参照してください。

    • az version を実行し、インストールされているバージョンおよび依存ライブラリを検索します。 最新バージョンにアップグレードするには、az upgrade を実行します。

  • この記事では、Azure CLI のバージョン 2.0.55 以降が必要です。 Azure Cloud Shell を使用している場合は、最新バージョンが既にインストールされています。

Caddyfile を準備する

Caddyfile というファイルを作成し、次の構成を貼り付けます。 この構成では、5000/TCP でリッスンしているアプリケーション コンテナーを指すリバース プロキシ構成が作成されます。

my-app.westeurope.azurecontainer.io {
    reverse_proxy http://localhost:5000
}

構成は、IP アドレスではなくドメイン名を参照することに注意することが重要です。 ACME プロトコルで必要なチャレンジ ステップを実行し、Let's Encrypt から証明書を正常に取得するには、Caddy はこの URL で到達できる必要があります。

Note

運用環境のデプロイでは、ユーザーは自分が制御するドメイン名 (例: api.company.com) を使用して、たとえば my-app.westeurope.azurecontainer.io を指す CNAME レコードを作成したい場合があります。 その場合、カスタム ドメイン名は Caddyfile 内でも Azure によって割り当てられた名前 (例: *.westeurope.azurecontainer.io) の代わりに使用されるようにする必要があります。 さらに、カスタム ドメイン名は、この例で後述する ACI YAML 構成内で参照される必要があります。

ストレージ アカウントの準備

ストレージ アカウントの作成

az storage account create \
  --name <storage-account> \
  --resource-group <resource-group> \
  --location westeurope

環境変数に接続文字列を格納する

AZURE_STORAGE_CONNECTION_STRING=$(az storage account show-connection-string --name <storage-account> --resource-group <resource-group> --output tsv)

コンテナーの状態と Caddy 構成を格納するために必要なファイル共有を作成します。

az storage share create \
  --name proxy-caddyfile \
  --account-name <storage-account>

az storage share create \
  --name proxy-config \
  --account-name <storage-account>
  
  az storage share create \
  --name proxy-data \
  --account-name <storage-account>

ストレージ アカウント キーを取得し、後で使用できるようにメモします

az storage account keys list -g <resource-group> -n <storage-account>

コンテナー グループをデプロイする

YAML ファイルを作成する

ci-my-app.yaml というファイルを作成し、次のコンテンツを貼り付けます。 <account-key> を、以前に受信したアクセス キーのどれかで置き換え、<storage-account> もそれに応じて置き換えてください。

この YAML ファイルでは、reverse-proxymy-app という 2 つのコンテナーが定義されています。 reverse-proxy コンテナーは、前に作成した 3 つのファイル共有をマウントします。 この構成では、reverse-proxy コンテナーのポート 80/TCP と 443/TCP も公開されます。 両方のコンテナー間の通信は localhost 上でのみ行われます。

Note

注意するべき重要な点として、dnsNameLabel キーは、パブリック DNS 名を定義し、その下でコンテナー インスタンス グループは到達可能になり、この DNS 名は Caddyfile 内で定義されている FQDN と一致する必要があります

name: ci-my-app
apiVersion: "2021-10-01"
location: westeurope
properties:
  containers:
    - name: reverse-proxy
      properties:
        image: caddy:2.6
        ports:
          - protocol: TCP
            port: 80
          - protocol: TCP
            port: 443
        resources:
          requests:
            memoryInGB: 1.0
            cpu: 1.0
          limits:
            memoryInGB: 1.0
            cpu: 1.0
        volumeMounts:
          - name: proxy-caddyfile
            mountPath: /etc/caddy
          - name: proxy-data
            mountPath: /data
          - name: proxy-config
            mountPath: /config
    - name: my-app
      properties:
        image: mcr.microsoft.com/azuredocs/aci-helloworld
        ports:
        - port: 5000
          protocol: TCP
        environmentVariables:
        - name: PORT
          value: 5000
        resources:
          requests:
            memoryInGB: 1.0
            cpu: 1.0
          limits:
            memoryInGB: 1.0
            cpu: 1.0
  ipAddress:
    ports:
      - protocol: TCP
        port: 80
      - protocol: TCP
        port: 443
    type: Public        
    dnsNameLabel: my-app
  osType: Linux
  volumes:
    - name: proxy-caddyfile
      azureFile: 
        shareName: proxy-caddyfile
        storageAccountName: "<storage-account>" 
        storageAccountKey: "<account-key>"
    - name: proxy-data
      azureFile: 
        shareName: proxy-data
        storageAccountName: "<storage-account>"  
        storageAccountKey: "<account-key>"
    - name: proxy-config
      azureFile: 
        shareName: proxy-config
        storageAccountName: "<storage-account>"  
        storageAccountKey: "<account-key>"

コンテナー グループをデプロイする

az group create コマンドでリソース グループを作成します。

az group create --name <resource-group> --location westeurope

az container create コマンドでコンテナー グループをデプロイし、YAML ファイルを引数として渡します。

az container create --resource-group <resource-group> --file ci-my-app.yaml

デプロイの状態を確認する

デプロイの状態を表示するには、次の az container show コマンドを使用します。

az container show --resource-group <resource-group> --name ci-my-app --output table

TLS 接続を検証する

すべてがうまくいったかどうかを確認する前に、コンテナー グループが完全に開始し、Caddy が証明書を要求するまである程度の時間待ちます。

OpenSSL

そのために、OpenSSL の s_client サブコマンドを使用できます。

echo "Q" | openssl s_client -connect my-app.westeurope.azurecontainer.io:443
CONNECTED(00000188)
---
Certificate chain
 0 s:CN = my-app.westeurope.azurecontainer.io
   i:C = US, O = Let's Encrypt, CN = R3
 1 s:C = US, O = Let's Encrypt, CN = R3
   i:C = US, O = Internet Security Research Group, CN = ISRG Root X1
 2 s:C = US, O = Internet Security Research Group, CN = ISRG Root X1
   i:O = Digital Signature Trust Co., CN = DST Root CA X3
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIEgTCCA2mgAwIBAgISAxxidSnpH4vVuCZk9UNG/pd2MA0GCSqGSIb3DQEBCwUA
MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD
EwJSMzAeFw0yMzA0MDYxODAzMzNaFw0yMzA3MDUxODAzMzJaMC4xLDAqBgNVBAMT
I215LWFwcC53ZXN0ZXVyb3BlLmF6dXJlY29udGFpbmVyLmlvMFkwEwYHKoZIzj0C
AQYIKoZIzj0DAQcDQgAEaaN/wGyFcimM+1O4WzbFgO6vIlXxXqp9vgmLZHpFrNwV
aO8JbaB7hE+M5EAg34LDY80RyHgY+Ff4vTh2Z96rVqOCAl4wggJaMA4GA1UdDwEB
/wQEAwIHgDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/
BAIwADAdBgNVHQ4EFgQUoL5DP+4PWiyE79hL5o+v8uymHdAwHwYDVR0jBBgwFoAU
FC6zF7dYVsuuUAlA5h+vnYsUwsYwVQYIKwYBBQUHAQEESTBHMCEGCCsGAQUFBzAB
hhVodHRwOi8vcjMuby5sZW5jci5vcmcwIgYIKwYBBQUHMAKGFmh0dHA6Ly9yMy5p
LmxlbmNyLm9yZy8wLgYDVR0RBCcwJYIjbXktYXBwLndlc3RldXJvcGUuYXp1cmVj
b250YWluZXIuaW8wTAYDVR0gBEUwQzAIBgZngQwBAgEwNwYLKwYBBAGC3xMBAQEw
KDAmBggrBgEFBQcCARYaaHR0cDovL2Nwcy5sZXRzZW5jcnlwdC5vcmcwggEEBgor
BgEEAdZ5AgQCBIH1BIHyAPAAdgC3Pvsk35xNunXyOcW6WPRsXfxCz3qfNcSeHQmB
Je20mQAAAYdX8+CQAAAEAwBHMEUCIQC9Ztqd3DXoJhOIHBW+P7ketGrKlVA6nPZl
9CiOrn6t8gIgXHcrbBqItemndRMv+UJ3DaBfTkYOqECecOJCgLhSYNUAdgDoPtDa
PvUGNTLnVyi8iWvJA9PL0RFr7Otp4Xd9bQa9bgAAAYdX8+CAAAAEAwBHMEUCIBJ1
24z44vKFUOLCi1a7ymVuWErkmLb/GtysvcxILaj0AiEAr49hyKfen4BbSTwC8Fg4
/LgZnn2F3uHI+9p+ZMO9xTAwDQYJKoZIhvcNAQELBQADggEBACqxa21eiW3JrZwk
FHgpd6SxhUeecrYXxFNva1Y6G//q2qCmGeKK3GK+ZGPqDtcoASH5t5ghV4dIT4WU
auVDLFVywXzR8PT6QUu3W8QxU+W7406twBf23qMIgrF8PIWhStI5mn1uCpeqlnf5
HpRaj2f5/5n19pcCZcrRx94G9qhPYdMzuy4mZRhxXRqrpIsabqX3DC2ld8dszCvD
pkV61iuARgm3MIQz1yL/x5Bn4nywjnhYZA4KFktC0Ti55cPRh1mkzGQAsYQDdWrq
dVav+U9dOLQ4Sq4suaDmzDzApr+hpQSJhwgRN16+tLMyZ6INAU2JWKDxiyDTdOuH
jz456og=
-----END CERTIFICATE-----
subject=CN = my-app.westeurope.azurecontainer.io

issuer=C = US, O = Let's Encrypt, CN = R3

---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: ECDSA
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 4208 bytes and written 401 bytes
Verification error: unable to get local issuer certificate
---
New, TLSv1.3, Cipher is TLS_AES_128_GCM_SHA256
Server public key is 256 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 20 (unable to get local issuer certificate)
---
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
    Protocol  : TLSv1.3
    Cipher    : TLS_AES_128_GCM_SHA256
    Session-ID: 85F1A4290F99A0DD28C8CB21EF4269E7016CC5D23485080999A8548057729B24
    Session-ID-ctx: 
    Resumption PSK: 752D438C19A5DBDBF10781F863D5E5D9A8859230968A9EAFFF7BBA86937D004F
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 604800 (seconds)
    TLS session ticket:
    0000 - 2f 25 98 90 9d 46 9b 01-03 78 db bd 4d 64 b3 a6   /%...F...x..Md..
    0010 - 52 c0 7a 8a b6 3d b8 4b-c0 d7 fc 04 e8 63 d4 bb   R.z..=.K.....c..
    0020 - 15 b3 25 b7 be 64 3d 30-2b d7 dc 7a 1a d1 22 63   ..%..d=0+..z.."c
    0030 - 42 30 90 65 6b b5 e1 83-a3 6c 76 c8 f6 ae e9 31   B0.ek....lv....1
    0040 - 45 91 33 57 8e 9f 4b 6a-2e 2c 9b f9 87 5f 71 1d   E.3W..Kj.,..._q.
    0050 - 5a 84 59 50 17 31 1f 62-2b 0e 1e e5 70 03 d9 e9   Z.YP.1.b+...p...
    0060 - 50 1c 5d 1f a4 3c 8a 0e-f4 c5 7d ce 9e 5c 98 de   P.]..<....}..\..
    0070 - e5                                                .

    Start Time: 1680808973
    Timeout   : 7200 (sec)
    Verify return code: 20 (unable to get local issuer certificate)
    Extended master secret: no
    Max Early Data: 0
---
read R BLOCK

Chrome ブラウザー

https://my-app.westeurope.azurecontainer.io に移動して URL の 横にある南京錠をクリックすることで、証明書を確認します。

証明書を確認する URL の横にある南京錠をハイライトしているスクリーンショット。

証明書の詳細を表示するには、"証明書が有効です" の前にある "接続はセキュリティで保護されています" をクリックします。

Let's Encrypt によって発行される証明書のスクリーンショット

次のステップ