Como usar vários certificados SSL no balanceamento de carga HTTPS com a Entrada

Nesta página, mostramos como usar vários certificados SSL para Entrada com balanceamento de carga interno e externo.

Visão geral

Se você quiser aceitar solicitações HTTPS dos seus clientes, o balanceador de carga HTTP(S) interno ou externo precisará ter um certificado para provar sua identidade a eles. Além disso, ele precisa ter uma chave privada para concluir o handshake HTTPS.

Quando o balanceador de carga aceita uma solicitação HTTPS de um cliente, o tráfego entre o cliente e o balanceador de carga é criptografado usando TLS. No entanto, o balanceador de carga encerra a criptografia TLS e encaminha a solicitação sem criptografia para o aplicativo. Ao configurar um balanceador de carga HTTP(S) por meio do Entrada, é possível defini-lo para apresentar até dez certificados TLS ao cliente.

O balanceador de carga usa a indicação de nome do servidor (SNI, na sigla em inglês) para determinar qual certificado precisa ser apresentado ao cliente com base no nome de domínio no handshake de TLS. Se o cliente não usar a SNI ou se o cliente usar um nome de domínio que não corresponda ao nome comum (CN, na sigla em inglês) em um dos certificados, o balanceador de carga usará o primeiro certificado listado em Entrada. O diagrama a seguir descreve o tráfego de envio do balanceador de carga para diferentes back-ends, dependendo do nome de domínio usado na solicitação:

Vários certificados SSL com diagrama de sistema da Entrada

Forneça um balanceador de carga HTTPS com certificados SSL usando um destes três métodos:

  • Certificados SSL gerenciados pelo Google. Consulte a página de certificados gerenciados para mais informações sobre como usá-los.

  • Certificado SSL do Google Cloud que você mesmo gerencia. Ele usa um certificado pré-compartilhado enviado anteriormente para seu projeto do Google Cloud.

  • Secrets do Kubernetes. O secret contém um certificado e uma chave que você mesmo cria. Para usar um secret, adicione o nome dele ao campo tls do seu manifesto de Entrada.

É possível usar mais de um método na mesma Entrada. Isso permite migrações sem tempo de inatividade entre os métodos.

Versão mínima do GKE

Você precisa ter a versão 1.10.2 ou posterior do GKE para usar certificados pré-compartilhados ou especificar vários certificados em uma Entrada.

Pré-requisitos

Para fazer os exercícios desta página, você precisa ter dois nomes de domínio. É preciso usar o Google Domains ou outro registrador.

Visão geral

Veja abaixo uma visão geral das etapas deste tópico:

  1. Crie uma implantação.

  2. Crie um serviço.

  3. Crie dois arquivos de certificado e dois arquivos de chaves ou dois objetos ManagedCertificate. Configure esses certificados no mesmo projeto e mesmo namespace em que o balanceador de carga está implantado.

  4. Crie um Ingress que use as chaves secretas ou os certificados pré-compartilhados. Como resultado da criação da Entrada, o GKE irá criar e configurar um balanceador de carga HTTP(S).

  5. Teste o balanceador de carga HTTP(S).

Antes de começar

Antes de começar, veja se você realizou as seguintes tarefas:

  • Verifique se você ativou a API do Google Kubernetes Engine.
  • Ativar a API Google Kubernetes Engine
  • Verifique se o SDK do Cloud está instalado.
  • Defina as configurações padrão da ferramenta de linha de comando gcloud do seu projeto usando um dos seguintes métodos:
    • Use gcloud init se quiser orientações para definir os padrões do projeto.
    • Use gcloud config para definir individualmente a região, a zona e o ID do projeto.

    gcloud init

    1. Execute gcloud init e siga as instruções:

      gcloud init

      Se você estiver usando SSH em um servidor remoto, utilize a sinalização --console-only para impedir que o comando inicie um navegador:

      gcloud init --console-only
    2. Siga as instruções para autorizar a ferramenta gcloud a usar sua conta do Google Cloud.
    3. Crie uma nova configuração ou selecione uma atual.
    4. Escolha um projeto do Google Cloud.
    5. Escolha uma zona padrão do Compute Engine.
    6. Escolha uma região padrão do Compute Engine.

    gcloud config

    1. Defina o ID do projeto padrão:
      gcloud config set project PROJECT_ID
    2. Defina a região padrão do Compute Engine (por exemplo, us-central1):
      gcloud config set compute/region COMPUTE_REGION
    3. Defina a zona padrão do Compute Engine (por exemplo, us-central1-c):
      gcloud config set compute/zone COMPUTE_ZONE
    4. Atualize gcloud para a versão mais recente:
      gcloud components update

    Ao definir locais padrão, é possível evitar erros na ferramenta gcloud como o seguinte: One of [--zone, --region] must be supplied: Please specify location.

Como criar uma implantação

Veja aqui um manifesto de uma implantação:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-mc-deployment
spec:
  selector:
    matchLabels:
      app: products
      department: sales
  replicas: 3
  template:
    metadata:
      labels:
        app: products
        department: sales
    spec:
      containers:
      - name: hello
        image: "us-docker.pkg.dev/google-samples/containers/gke/hello-app:1.0"
        env:
        - name: "PORT"
          value: "50001"
      - name: hello-again
        image: "us-docker.pkg.dev/google-samples/containers/gke/hello-app:2.0"
        env:
        - name: "PORT"
          value: "50002"

A implantação tem três pods e cada pod tem dois contêineres. Um contêiner executa hello-app:1.0 e escuta a porta TCP 50001. O outro contêiner executa hello-app:2.0 e escuta a porta TCP 50002.

Copie o manifesto em um arquivo denominado my-mc-deployment.yaml e crie a implantação:

kubectl apply -f my-mc-deployment.yaml

Como criar um serviço

Veja aqui um manifesto de um serviço.

apiVersion: v1
kind: Service
metadata:
  name: my-mc-service
spec:
  type: NodePort
  selector:
    app: products
    department: sales
  ports:
  - name: my-first-port
    protocol: TCP
    port: 60001
    targetPort: 50001
  - name: my-second-port
    protocol: TCP
    port: 60002
    targetPort: 50002

O campo selector no manifesto do serviço diz que qualquer pod que tenha o rótulo app: products e o rótulo department: sales é membro do serviço. Assim, os pods da implantação que você criou na etapa anterior são membros do serviço.

O campo ports do manifesto de Service é uma matriz de objetos ServicePort (em inglês). Quando um cliente envia uma solicitação ao serviço em my-first-port, a solicitação é encaminhada a um dos pods de membro na porta 50001. Quando um cliente envia uma solicitação para o serviço em my-second-port, a solicitação é encaminhada a um dos pods de membro na porta 50002.

Copie o manifesto para um arquivo chamado my-mc-service.yaml e crie o serviço:

kubectl apply -f my-mc-service.yaml

Como criar certificados e chaves

Para fazer os exercícios desta página, você precisa de dois certificados, cada um com uma chave correspondente. Cada certificado precisa ter um Nome comum (CN, na sigla em inglês) que seja igual a um nome de domínio que você tem. É possível criar esses certificados manualmente ou usar os gerenciados pelo Google. Se você já tiver dois arquivos de certificado com os valores apropriados para o nome comum, avance para a próxima seção.

Certificados gerenciados pelo usuário

  1. Crie a primeira chave:

    openssl genrsa -out test-ingress-1.key 2048
    
  2. Crie a primeira solicitação de assinatura de certificado:

    openssl req -new -key test-ingress-1.key -out test-ingress-1.csr \
        -subj "/CN=FIRST_DOMAIN"
    

    Substitua FIRST_DOMAIN por um nome de domínio de sua propriedade.

    Por exemplo, suponha que você queira que o balanceador de carga exiba solicitações do domínio example.com. Nesse caso, sua solicitação de assinatura de certificado ficaria assim:

    openssl req -new -key test-ingress-1.key -out test-ingress-1.csr \
        -subj "/CN=example.com"
    
  3. Crie o primeiro certificado:

    openssl x509 -req -days 365 -in test-ingress-1.csr -signkey test-ingress-1.key \
        -out test-ingress-1.crt
    
  4. Crie a segunda chave:

    openssl genrsa -out test-ingress-2.key 2048
    
  5. Crie a segunda solicitação de assinatura de certificado:

    openssl req -new -key test-ingress-2.key -out test-ingress-2.csr \
        -subj "/CN=SECOND_DOMAIN"
    

    Substitua SECOND_DOMAIN por outro nome de domínio que seja seu.

    Por exemplo, suponha que você queira que o balanceador de carga exiba solicitações do domínio examplepetstore.com. Nesse caso, sua solicitação de assinatura de certificado ficaria assim:

    openssl req -new -key test-ingress-2.key -out test-ingress-2.csr \
        -subj "/CN=examplepetstore.com"
    
  6. Crie o segundo certificado:

    openssl x509 -req -days 365 -in test-ingress-2.csr -signkey test-ingress-2.key \
        -out test-ingress-2.crt
    

Para mais informações sobre certificados e chaves, consulte a Visão geral dos certificados SSL.

Agora você tem dois arquivos de certificado e dois arquivos de chaves.

As demais tarefas usam os seguintes marcadores para referir-se aos seus domínios, certificados e chaves:

  • FIRST_CERT_FILE: o caminho para o primeiro arquivo de certificado.
  • FIRST_KEY_FILE: o caminho para o arquivo de chave que acompanha o primeiro certificado.
  • FIRST_DOMAIN: um nome de domínio que pertence a você.
  • FIRST_SECRET_NAME: o nome do secret que contém o primeiro certificado e chave.
  • SECOND_CERT_FILE: o caminho para o segundo arquivo de certificado.
  • SECOND_KEY_FILE: o caminho para o arquivo de chave que acompanha o segundo certificado.
  • SECOND_DOMAIN: um segundo nome de domínio que você tem.
  • SECOND_SECRET_NAME: o nome do secret que contém o segundo certificado e chave.

Certificados gerenciados pelo Google

Para criar certificados gerenciados pelo Google, adicione objetos ManagedCertificate ao namespace da Entrada. É possível usar o seguinte modelo para definir certificados para seus domínios:

  apiVersion: networking.gke.io/v1
  kind: ManagedCertificate
  metadata:
    name: FIRST_CERT_NAME
  spec:
    domains:
      - FIRST_DOMAIN
  ---
  apiVersion: networking.gke.io/v1
  kind: ManagedCertificate
  metadata:
    name: SECOND_CERT_NAME
  spec:
    domains:
      - SECOND_DOMAIN

Substitua:

  • FIRST_CERT_NAME: o nome do primeiro objeto ManagedCertificate.
  • FIRST_DOMAIN: o primeiro domínio que pertence a você.
  • SECOND_CERT_NAME: o nome do segundo objeto ManagedCertificate.
  • SECOND_DOMAIN: o segundo domínio que pertence a você.

Como especificar certificados para a Entrada

A próxima etapa é criar um objeto Entrada. No manifesto da Entrada, use um dos seguintes métodos para fornecer certificados ao balanceador de carga:

  • Secrets
  • Certificados pré-compartilhados
  • Certificados gerenciados pelo Google

Escolha um dos métodos selecionando uma das guias:

Secrets

Como criar secrets

  1. Crie um secret que contenha seu primeiro certificado e chave:

    kubectl create secret tls FIRST_SECRET_NAME \
        --cert FIRST_CERT_FILE --key FIRST_KEY_FILE
    
  2. Crie um secret que contenha seu segundo certificado e chave:

    kubectl create secret tls SECOND_SECRET_NAME \
        --cert SECOND_CERT_FILE --key SECOND_KEY_FILE
    

Como criar um Ingress

Veja aqui o manifesto de um Ingress.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-mc-ingress
spec:
  tls:
  - secretName: FIRST_SECRET_NAME
  - secretName: SECOND_SECRET_NAME
  rules:
  - host: FIRST_DOMAIN
    http:
      paths:
      - pathType: ImplementationSpecific
        backend:
          service:
            name: my-mc-service
            port:
              name: my-first-port
  - host: SECOND_DOMAIN
    http:
      paths:
      - pathType: ImplementationSpecific
        backend:
          service:
            name: my-mc-service
            port:
              name: my-second-port
  1. Copie o manifesto para um arquivo chamado my-mc-ingress.yaml. Substitua FIRST_DOMAIN e SECOND_DOMAIN pelos seus nomes de domínio, por exemplo, example.com e examplepetstore.com.

  2. Crie e Entrada:

    kubectl apply -f my-mc-ingress.yaml
    

    Quando você cria uma Entrada, o controlador de entrada do GKE cria um balanceador de carga HTTP(S). Aguarde um minuto para que o GKE atribua um endereço IP externo ao balanceador de carga.

  3. Descreva sua Entrada:

    kubectl describe ingress my-mc-ingress
    

    A resposta mostra que dois secrets estão associados ao Ingress. A saída também mostra o endereço IP externo do balanceador de carga:

    Name: my-mc-ingress
    Address: 203.0.113.1
    ...
    TLS:
      FIRST_SECRET_NAME terminates
      SECOND_SECRET_NAME terminates
    Rules:
      Host              Path  Backends
      ----              ----  --------
      FIRST_DOMAIN
                         my-mc-service:my-first-port (<none>)
      SECOND_DOMAIN
                         my-mc-service:my-second-port (<none>)
    Annotations:
    ...
    Events:
      Type    Reason  Age   From                     Message
      ----    ------  ----  ----                     -------
      Normal  ADD     3m    loadbalancer-controller  default/my-mc-ingress
      Normal  CREATE  2m    loadbalancer-controller  ip: 203.0.113.1
    

Certificados pré-compartilhados

Como criar certificados pré-compartilhados

  1. Crie um recurso de certificado no seu projeto do Google Cloud:

    gcloud compute ssl-certificates create FIRST_CERT_NAME \
        --certificate FIRST_CERT_FILE  --private-key FIRST_KEY_FILE
    

    Substitua:

  2. Crie um segundo recurso de certificado no projeto do Google Cloud:

    gcloud compute ssl-certificates create SECOND_CERT_NAME \
        --certificate SECOND_CERT_FILE --private-key SECOND_KEY_FILE
    

    Substitua:

    • SECOND_CERT_NAME: o nome do segundo certificado.
    • SECOND_CERT_FILE: o segundo arquivo de certificado.
    • SECOND_KEY_FILE: o segundo arquivo de chave.
  3. Veja seus recursos de certificado:

    gcloud compute ssl-certificates list
    

    A saída mostra que você tem recursos de certificado chamados FIRST_CERT_NAME e SECOND_CERT_NAME:

    NAME                   CREATION_TIMESTAMP
    FIRST_CERT_NAME      2018-11-03T12:08:47.751-07:00
    SECOND_CERT_NAME     2018-11-03T12:09:25.359-07:00
    

Como criar uma entrada do GKE

Veja aqui um manifesto de uma Entrada que lista recursos de certificado pré-compartilhados em uma anotação:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-psc-ingress
  annotations:
    ingress.gcp.kubernetes.io/pre-shared-cert: "FIRST_CERT_NAME,SECOND_CERT_NAME"
spec:
  rules:
  - host: FIRST_DOMAIN
    http:
      paths:
      - pathType: ImplementationSpecific
        backend:
          service:
            name: my-mc-service
            port:
              name: my-first-port
  - host: SECOND_DOMAIN
    http:
      paths:
      - pathType: ImplementationSpecific
        backend:
          service:
            name: my-mc-service
            port:
              name: my-second-port
  1. Copie o manifesto para um arquivo chamado my-psc-ingress.yaml. Substitua FIRST_DOMAIN e SECOND_DOMAIN pelos seus nomes de domínio.

  2. Crie e Entrada:

    kubectl apply -f my-psc-ingress.yaml
    

    Aguarde um minuto para que o GKE atribua um endereço IP externo ao balanceador de carga.

  3. Descreva o Ingress:

    kubectl describe ingress my-psc-ingress
    

    A saída mostra que a Entrada está associada a certificados compartilhados anteriormente chamados FIRST_CERT_NAME e SECOND_CERT_NAME. A saída também mostra o endereço IP externo do balanceador de carga:

    Name:             my-psc-ingress
    Address:          203.0.113.2
    ...
    Rules:
      Host              Path  Backends
      ----              ----  --------
      FIRST_DOMAIN
                         my-mc-service:my-first-port (<none>)
      SECOND_DOMAIN
                         my-mc-service:my-second-port (<none>)
    Annotations:
      ...
      ingress.gcp.kubernetes.io/pre-shared-cert:    FIRST_CERT_NAME,SECOND_CERT_NAME
      ...
      ingress.kubernetes.io/ssl-cert:               FIRST_CERT_NAME,SECOND_CERT_NAME
    Events:
      Type    Reason  Age   From                     Message
      ----    ------  ----  ----                     -------
      Normal  ADD     2m    loadbalancer-controller  default/my-psc-ingress
      Normal  CREATE  1m    loadbalancer-controller  ip: 203.0.113.2
    

Certificados gerenciados pelo Google

Como criar uma entrada do GKE

Veja aqui um manifesto de uma Entrada que lista recursos de certificado pré-compartilhados em uma anotação:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-gmc-ingress
  annotations:
    networking.gke.io/managed-certificates: "FIRST_CERT_NAME,SECOND_CERT_NAME"
spec:
  rules:
  - host: FIRST_DOMAIN
    http:
      paths:
      - pathType: ImplementationSpecific
        backend:
          service:
            name: my-mc-service
            port:
              name: my-first-port
  - host: SECOND_DOMAIN
    http:
      paths:
      - pathType: ImplementationSpecific
        backend:
          service:
            name: my-mc-service
            port:
              name: my-second-port
  1. Copie o manifesto para um arquivo chamado my-gmc-ingress.yaml. Substitua FIRST_DOMAIN e SECOND_DOMAIN pelos seus nomes de domínio.

  2. Crie e Entrada:

    kubectl apply -f my-gmc-ingress.yaml
    

    Aguarde um minuto para que o GKE atribua um endereço IP externo ao balanceador de carga.

  3. Descreva o Ingress:

    kubectl describe ingress my-gmc-ingress
    

    A saída mostra que a Entrada está associada a certificados gerenciados chamados FIRST_CERT_NAME e SECOND_CERT_NAME. O GKE preenche automaticamente as anotações ingress.gcp.kubernetes.io/pre-shared-cert e ingress.kubernetes.io/ssl-cert para apontar para os certificados gerenciados pelo Google que você criou usando os objetos ManagedCertificate. A saída também mostra o endereço IP do balanceador de carga:

    Name:             my-gmc-ingress
    Address:          203.0.113.2
    ...
    Rules:
      Host              Path  Backends
      ----              ----  --------
      FIRST_DOMAIN
                         my-mc-service:my-first-port (<none>)
      SECOND_DOMAIN
                         my-mc-service:my-second-port (<none>)
    Annotations:
      ...
      ingress.gcp.kubernetes.io/pre-shared-cert:    mcrt-a6e41ce4-2b39-4334-84ce-867ff543c424,mcrt-bbff4116-f014-4800-a43a-4095bffeb4f4
      ...
      ingress.kubernetes.io/ssl-cert:               mcrt-a6e41ce4-2b39-4334-84ce-867ff543c424,mcrt-bbff4116-f014-4800-a43a-4095bffeb4f4
      networking.gke.io/managed-certificates:       FIRST_CERT_NAME,SECOND_CERT_NAME
    Events:
      Type    Reason  Age   From                     Message
      ----    ------  ----  ----                     -------
      Normal  ADD     2m    loadbalancer-controller  default/my-gmc-ingress
      Normal  CREATE  1m    loadbalancer-controller  ip: 203.0.113.2
    

Como testar o balanceador de carga

Aguarde cerca de cinco minutos para que o GKE conclua a configuração do balanceador de carga. No caso de certificados gerenciados pelo Google, pode levar mais tempo para concluir a configuração, pois o sistema precisa provisionar os certificados e verificar a configuração de DNS para determinados domínios.

Nesta etapa, você precisa ter dois nomes de domínio que precisam resolver o endereço IP externo do balanceador de carga HTTP(S).

Envie uma solicitação ao balanceador de carga usando seu primeiro nome de domínio:

curl -v https://FIRST_DOMAIN

A saída mostra que o primeiro certificado foi usado no handshake de TLS. Se seu primeiro domínio for example.com, a resposta será semelhante a esta:

...
*   Trying 203.0.113.1...
...
* Connected to example.com (203.0.113.1) port 443 (#0)
...
* TLSv1.2 (IN), TLS handshake, Certificate (11):
...
* Server certificate:
*  subject: CN=example.com
...
> Host: example.com
...
Hello, world!
Version: 1.0.0
...

Envie uma solicitação ao balanceador de carga usando o segundo nome de domínio:

curl -v https://SECOND_DOMAIN

A saída mostra que o segundo certificado foi usado no handshake de TLS. Se seu segundo domínio for examplepetstore.com, a resposta será semelhante a esta:

...
*   Trying 203.0.113.1...
...
* Connected to examplepetstore.com (203.0.113.1) port 443 (#0)
...
* Server certificate:
*  subject: CN=examplepetstore.com
...
> Host: examplepetstore.com
...
Hello, world!
Version: 2.0.0

O campo hosts de um objeto Entrada

No IngressSpec há um campo tls que é uma matriz de objetos IngressTLS (links em inglês). Cada objeto IngressTLS tem um campo hosts e um campo SecretName. No GKE, o campo hosts não é usado. O GKE lê o nome comum (CN, na sigla em inglês) do certificado no secret. Se o Nome comum corresponder ao nome de domínio em uma solicitação do cliente, o balanceador de carga apresentará o certificado correspondente ao cliente.

Qual certificado é apresentado?

O balanceador de carga escolhe um certificado de acordo com estas regras:

  • Se os Secrets e os certificados pré-compartilhados estiverem listados na Entrada, os certificados pré-compartilhados terão prioridade sobre os Secrets. Ou seja, os Secrets ainda serão incluídos, mas os certificados pré-compartilhados serão apresentados primeiro.

  • Se nenhum certificado tiver um nome comum que corresponda ao nome de domínio na solicitação do cliente, o balanceador de carga apresentará o certificado principal.

  • Para os secrets listados no bloco tls, o certificado principal está no primeiro secret da lista.

  • Para certificados pré-compartilhados listados na anotação, o certificado principal é o primeiro certificado na lista.

Práticas recomendadas de rotação de certificados

Se você quiser fazer a rotação do conteúdo do certificado (Secret ou pré-compartilhado), veja algumas práticas recomendadas:

  • Crie um novo Secret ou certificado pré-compartilhado com um nome diferente que contenha os novos dados do certificado. Anexe esse recurso, com o atual, à Entrada usando as instruções fornecidas anteriormente. Quando as alterações forem finalizadas, será possível remover o certificado antigo da entrada.
  • Se você não se importar de interromper o tráfego, poderá remover o recurso antigo da entrada, provisionar um novo recurso com o mesmo nome, mas com conteúdo diferente, e anexá-lo novamente à Entrada.

Para evitar gerenciar a rotação de certificados por conta própria, consulte o recurso SSL gerenciado pelo Google.

Solução de problemas

A especificação de chaves secretas inválidas ou inexistentes resulta em um erro de evento do Kubernetes. Veja os eventos do Kubernetes para verificar se há uma Entrada como esta:

kubectl describe ingress

Esta saída é semelhante a:

Name:             my-ingress
Namespace:        default
Address:          203.0.113.3
Default backend:  hello-server:8080 (10.8.0.3:8080)
TLS:
  my-faulty-Secret terminates
Rules:
  Host  Path  Backends
  ----  ----  --------
  *     *     my-service:443 (10.8.0.3:443)
Events:
   Error during sync: cannot get certs for Ingress default/my-ingress:
 Secret "my-faulty-ingress" has no 'tls.crt'

A seguir