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


Nesta página, mostramos como configurar vários certificados SSL para recursos de Entrada em clusters do Google Kubernetes Engine (GKE).

Visão geral

Se você quiser aceitar solicitações HTTPS dos seus clientes, o balanceador de carga de aplicativo precisará ter um certificado para provar a identidade dele aos clientes. 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 de aplicativo 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 mostra 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

É possível fornecer um balanceador de carga de aplicativo com certificados SSL usando os seguintes métodos:

  • Certificado SSL do Google Cloud que você gerencia por conta própria. O certificado SSL usa um certificado pré-compartilhado que você envia para o projeto do Google Cloud.

  • Secrets do Kubernetes. O secret contém um certificado e uma chave que você mesmo cria. Adicione o nome do secret ao campo tls do manifesto da Entrada.

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

Visão geral

Aqui está uma visão geral das etapas deste documento:

  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 no mesmo namespace de onde o balanceador de carga é implantado.

  4. Crie um Ingress que use as chaves secretas ou os certificados pré-compartilhados. Quando você cria a Entrada, o GKE cria e configura um balanceador de carga de aplicativo.

  5. Teste o balanceador de carga de aplicativo.

Antes de começar

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

  • Ative a API Google Kubernetes Engine.
  • Ativar a API Google Kubernetes Engine
  • Se você quiser usar a CLI do Google Cloud para essa tarefa, instale e, em seguida, inicialize a CLI gcloud. Se você instalou a gcloud CLI anteriormente, instale a versão mais recente executando gcloud components update.
  • Você precisa ter dois nomes de domínio. Os nomes do domínios não podem ter mais de 63 caracteres.

Limitações

  • Os certificados gerenciados pelo Google são compatíveis apenas com o Ingress do GKE que usa o balanceador de carga de aplicativo externo. Os certificados gerenciados pelo Google não são compatíveis com controladores de entrada de terceiros.
  • Para balanceadores de carga de aplicativo internos, é preciso desativar o HTTP no manifesto do Ingress. Ele não é necessário para o balanceador de carga externo.
  • Não altere ou atualize manualmente a configuração do balanceador de carga de aplicativo. Isso significa que não é possível editar nenhum dos componentes do balanceador de carga, incluindo proxies de destino, mapas de URL e serviços de back-end. Todas as alterações feitas são substituídas pelo GKE.

Crie uma implantação

  1. Salve o seguinte manifesto como my-mc-deployment.yaml:

    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"
    

    Esse manifesto descreve uma implantação com três pods. 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.

  2. Aplique o manifesto ao cluster:

    kubectl apply -f my-mc-deployment.yaml
    

Crie um serviço.

  1. Salve o seguinte manifesto como my-mc-service.yaml:

    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
    

    Este manifesto descreve um Serviço com os seguintes campos:

    • selector: especifica que qualquer pod que tenha os rótulos app: products e department: sales é membro desse serviço.
    • ports: especifica que, quando um cliente envia uma solicitação ao serviço em my-first-port, o GKE a encaminha para um dos pods de membro na porta 50001. Quando um cliente envia uma solicitação ao serviço em my-second-port, o GKE encaminha a solicitação para um dos pods de membro na porta 50002.
  2. Aplique o manifesto ao cluster:

    kubectl apply -f my-mc-service.yaml
    

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, como 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 de sua propriedade, como 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ê.

Os nomes dos objetos ManagedCertificate são diferentes dos nomes dos certificados reais que eles criam. Você só precisará saber os nomes dos objetos ManagedCertificate para usá-los na Entrada.

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

Secrets

  1. Crie uma chave secreta 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
    

Criar uma Entrada

  1. Salve o seguinte manifesto como my-mc-ingress.yaml:

    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:
                  number: 60001
      - host: SECOND_DOMAIN
        http:
          paths:
          - pathType: ImplementationSpecific
            backend:
              service:
                name: my-mc-service
                port:
                  number: 60002
    

    Substitua FIRST_DOMAIN e SECOND_DOMAIN pelos seus nomes de domínio, por exemplo, example.com e examplepetstore.com.

  2. Aplique o manifesto ao cluster:

    kubectl apply -f my-mc-ingress.yaml
    
  3. Descreva o Ingress:

    kubectl describe ingress my-mc-ingress
    

    O resultado será assim:

    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
    

    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: Se o endereço IP externo não estiver definido, aguarde alguns minutos e tente o comando novamente.

Certificados pré-compartilhados

  1. Criar um certificado:

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

    Substitua:

  2. Crie um segundo certificado:

    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
    

    O resultado será assim:

    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
    

Criar uma Entrada

  1. Salve o seguinte manifesto como my-psc-ingress.yaml:

    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:
                  number: 60001
      - host: SECOND_DOMAIN
        http:
          paths:
          - pathType: ImplementationSpecific
            backend:
              service:
                name: my-mc-service
                port:
                  number: 60002
    

    Substitua FIRST_DOMAIN e SECOND_DOMAIN pelos seus nomes de domínio.

    Este manifesto descreve um Ingress que lista recursos de certificado pré-compartilhados em uma anotação.

  2. Aplique o manifesto ao cluster:

    kubectl apply -f my-psc-ingress.yaml
    
  3. Descreva o Ingress:

    kubectl describe ingress my-psc-ingress
    

    O resultado será assim:

    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
    

    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. Se o endereço IP externo não estiver definido, aguarde alguns minutos e tente o comando novamente.

Certificados gerenciados pelo Google

Criar uma Entrada

  1. Salve o seguinte manifesto como my-gmc-ingress.yaml:

    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:
                  number: 60001
      - host: SECOND_DOMAIN
        http:
          paths:
          - pathType: ImplementationSpecific
            backend:
              service:
                name: my-mc-service
                port:
                  number: 60002
    

    Substitua FIRST_DOMAIN e SECOND_DOMAIN pelos seus nomes de domínio.

    Este manifesto descreve um Ingress que lista recursos de certificado pré-compartilhados em uma anotação.

  2. Aplique o manifesto ao cluster:

    kubectl apply -f my-gmc-ingress.yaml
    
  3. Descreva o Ingress:

    kubectl describe ingress my-gmc-ingress
    

    O resultado será assim:

    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
    

    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 com os certificados gerenciados pelo Google que você criou usando os objetos ManagedCertificate. A saída também mostra o endereço IP externo do balanceador de carga. Se o endereço IP externo não estiver definido, aguarde alguns minutos e tente o comando novamente.

Testar o balanceador de carga

Aguarde cerca de cinco minutos para que o GKE conclua a configuração do balanceador de carga.

Se você usou 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.

Para testar o balanceador de carga, você precisa ter dois nomes de domínio, que precisam resolver o endereço IP externo do balanceador de carga de aplicativo externo.

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

    curl -v https://FIRST_DOMAIN
    

    Talvez seja necessário usar a opção curl -k para executar uma transferência SSL não segura. Assim, curl aceitará certificados autoassinados.

    O resultado será assim:

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

    A saída mostra que o primeiro certificado foi usado no handshake de TLS.

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

    curl -v https://SECOND_DOMAIN
    

    O resultado será assim:

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

    A saída mostra que o segundo certificado foi usado no handshake de TLS.

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 alternar o conteúdo do secret ou do certificado 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 Usar certificados SSL gerenciados 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

O resultado será assim:

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

  • Se você tiver um aplicativo em execução em vários clusters do GKE em regiões diferentes, configure uma Entrada em vários clusters para rotear o tráfego para um cluster na região mais próxima do usuário.