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.

  4. Crie uma Entrada 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, verifique se você realizou as tarefas a seguir:

Defina as configurações padrão da gcloud usando um dos métodos a seguir:

  • Use gcloud init se quiser orientações para definir os padrões.
  • Use gcloud config para definir individualmente a região, a zona e o ID do projeto.

Como usar o gcloud init

Se você receber o erro One of [--zone, --region] must be supplied: Please specify location, conclua esta seção.

  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 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 para clusters zonais ou uma região para clusters regionais ou de Autopilot.

Como usar o gcloud config

  • Defina o ID do projeto padrão:
    gcloud config set project PROJECT_ID
  • Se você estiver trabalhando com clusters zonais, defina a zona do Compute padrão:
    gcloud config set compute/zone COMPUTE_ZONE
  • Se você estiver trabalhando com clusters de Autopilot ou regionais, defina a região do Compute padrão:
    gcloud config set compute/region COMPUTE_REGION
  • Atualize gcloud para a versão mais recente:
    gcloud components update

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: "gcr.io/google-samples/hello-app:2.0"
        env:
        - name: "PORT"
          value: "50001"
      - name: hello-again
        image: "gcr.io/google-samples/node-hello:1.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 e escuta a porta TCP 50001. O outro contêiner executa node-hello 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. Se você já tiver dois arquivos de certificado com os valores apropriados para o nome comum, avance para a próxima seção.

Crie a primeira chave:

openssl genrsa -out test-ingress-1.key 2048

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"

em que first-domain é um nome de domínio que você tem.

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"

Crie o primeiro certificado:

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

Crie a segunda chave:

openssl genrsa -out test-ingress-2.key 2048

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"

second-domain é outro nome de domínio que você tem.

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"

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 seu 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.

Como especificar certificados para a Entrada

A próxima etapa é criar um objeto Entrada. No seu manifesto Entrada, é possível usar um dos dois métodos para fornecer certificados ao balanceador de carga:

  • Chaves secretas
  • Certificados pré-compartilhados

Escolha um dos dois métodos selecionando a guia SECRETS ou a guia PRE-SHARED CERTS:

Secrets

Como criar secrets

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

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/v1beta1
kind: Ingress
metadata:
  name: my-mc-ingress
spec:
  tls:
  - secretName: first-secret-name
  - secretName: second-secret-name
  rules:
  - host: first-domain
    http:
      paths:
      - backend:
          serviceName: my-mc-service
          servicePort: my-first-port
  - host: second-domain
    http:
      paths:
      - backend:
          serviceName: my-mc-service
          servicePort: my-second-port

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.

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.

Descreva sua Entrada:

kubectl describe ingress my-mc-ingress

A saída mostra que duas chaves secretas estão associadas à Entrada. 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
  ----              ----  --------
  example.com
                     my-mc-service:my-first-port (<none>)
  examplepetstore.com
                     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 usar certificados pré-compartilhados

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

gcloud compute ssl-certificates create test-ingress-1 \
--certificate first-cert-file --private-key first-key-file

onde:

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

gcloud compute ssl-certificates create test-ingress-2 \
--certificate second-cert-file --private-key second-key-file

em que:

  • second-cert-file é o segundo arquivo de certificado;
  • second-key-file é o segundo arquivo de chave.

Veja seus recursos de certificado:

gcloud compute ssl-certificates list

A saída mostra que você tem recursos de certificado chamados test-ingres-1 e test-ingress-2:

NAME                CREATION_TIMESTAMP
test-ingress-1      2018-11-03T12:08:47.751-07:00
test-ingress-2      2018-11-03T12:09:25.359-07:00

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

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: my-psc-ingress
  annotations:
    ingress.gcp.kubernetes.io/pre-shared-cert: "test-ingress-1,test-ingress-2"
spec:
  rules:
  - host: first-domain
    http:
      paths:
      - backend:
          serviceName: my-mc-service
          servicePort: my-first-port
  - host: second-domain
    http:
      paths:
      - backend:
          serviceName: my-mc-service
          servicePort: my-second-port

Copie o manifesto para um arquivo chamado my-psc-ingress.yaml. Substitua first-domain e second-domain pelos seus nomes de domínio.

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.

Descreva sua Entrada:

kubectl describe ingress my-psc-ingress

A saída mostra que a Entrada está associada a certificados compartilhados anteriormente chamados test-ingress-1 e test-ingress-2. 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
  ----              ----  --------
  example.com
                     my-mc-service:my-first-port (<none>)
  examplepetstore.com
                     my-mc-service:my-second-port (<none>)
Annotations:
  ...
  ingress.gcp.kubernetes.io/pre-shared-cert:    test-ingress-1,test-ingress-2
  ...
  ingress.kubernetes.io/ssl-cert:               test-ingress-1,test-ingress-2
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

Como testar o balanceador de carga

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

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 resposta 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
...
&lt;
Hello, world!
Version: 2.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 Kubernetes!

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