Share via


Ativar HTTPS automático com a Caddy num contentor de sidecar

Este artigo descreve como a Caddy pode ser utilizada como um contentor de sidecar num grupo de contentores que atua como um proxy inverso para fornecer um ponto final HTTPS gerido automaticamente para a sua aplicação.

A Caddy é um poderoso servidor Web preparado para empresas open source com HTTPS automático escrito em Go e representa uma alternativa ao Nginx.

A automatização de certificados é possível porque a Caddy suporta a API ACMEv2 (RFC 8555) que interage com Let's Encrypt para emitir certificados.

Neste exemplo, apenas o contentor Caddy é exposto nas portas 80/TCP e 443/TCP. A aplicação por trás do proxy inverso permanece privada. A comunicação de rede entre a Caddy e a sua aplicação ocorre através do localhost.

Nota

Isto contrasta com a comunicação entre grupos de contentores conhecida pela composição do docker, onde os contentores podem ser referenciados por nome.

O exemplo monta o Caddyfile, que é necessário para configurar o proxy inverso, a partir de uma partilha de ficheiros alojada numa conta de Armazenamento do Azure.

Nota

Para implementações de produção, a maioria dos utilizadores vai querer cozer o Caddyfile numa imagem personalizada do docker com base na caddy. Desta forma, não é necessário montar ficheiros no contentor.

Pré-requisitos

  • Este artigo requer a versão 2.0.55 ou posterior da CLI do Azure. Se utilizar o Azure Cloud Shell, a versão mais recente já está instalada.

Preparar o Caddyfile

Crie um ficheiro chamado Caddyfile e cole a seguinte configuração. Esta configuração cria uma configuração de proxy inverso, apontando para o contentor da aplicação a escutar em 5000/TCP.

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

É importante ter em atenção que a configuração referencia um nome de domínio em vez de um endereço IP. A Caddy tem de estar acessível por este URL para executar o passo de desafio exigido pelo protocolo ACME e obter com êxito um certificado da Let's Encrypt.

Nota

Para a implementação de produção, os utilizadores podem querer utilizar um nome de domínio que controlam, por exemplo, api.company.com e criar um registo CNAME que aponte para, por exemplo, my-app.westeurope.azurecontainer.io. Se assim for, tem de ser garantido que o nome de domínio personalizado também é utilizado no Caddyfile, em vez do atribuído pelo Azure (por exemplo, *.westeurope.azurecontainer.io). Além disso, o nome de domínio personalizado tem de ser referenciado na configuração do YAML do ACI descrita mais adiante neste exemplo.

Preparar conta de armazenamento

Criar uma conta de armazenamento

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

Armazenar a cadeia de ligação numa variável de ambiente

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

Crie as partilhas de ficheiros necessárias para armazenar o estado do contentor e a configuração 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>

Obter as chaves da conta de armazenamento e tomar nota para utilização posterior

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

Implementar grupo de contentores

Criar ficheiro YAML

Crie um ficheiro chamado ci-my-app.yaml e cole o seguinte conteúdo. Certifique-se de que substitui <account-key> por uma das chaves de acesso recebidas anteriormente e <storage-account> em conformidade.

Este ficheiro YAML define dois contentores reverse-proxy e my-app. O reverse-proxy contentor monta as três partilhas de ficheiros criadas anteriormente. A configuração também expõe a porta 80/TCP e 443/TCP do reverse-proxy contentor. A comunicação entre ambos os contentores ocorre apenas no localhost.

Nota

É importante ter em atenção que a dnsNameLabel chave define o nome DNS público, no qual o grupo de instâncias de contentor será acessível, tem de corresponder ao FQDN definido no Caddyfile

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>"

Implementar o grupo de contentores

Crie um grupo de recursos com o comando az group create :

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

Implemente o grupo de contentores com o comando az container create , transmitindo o ficheiro YAML como um argumento.

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

Ver o estado de implementação

Para ver o estado da implementação, utilize o seguinte comando az container show :

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

Verificar a ligação TLS

Antes de verificar se tudo correu bem, dê algum tempo ao grupo de contentores para começar totalmente e para que a Caddy peça um certificado.

OpenSSL

Podemos utilizar o s_client subcomando do OpenSSL para esse fim.

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

Browser Chrome

Navegue para https://my-app.westeurope.azurecontainer.io e verifique o certificado clicando no cadeado junto ao URL.

Captura de ecrã a realçar o cadeado junto ao URL que verifica o certificado.

Para ver os detalhes do certificado, clique em "A ligação é segura" seguida de "o certificado é válido".

Captura de ecrã do certificado emitido por Let's Encrypt

Passos seguintes