Vamos criptografar certificados SSL com o gerenciador de certificados no Kubernetes



Neste artigo, falarei sobre como automatizar o pedido e a renovação de certificados do Let's Encrypt (e não apenas) para o Ingress no Kubernetes usando o gerenciador de certificados adicional. Mas vou começar com uma breve introdução à essência do problema.

Um pouco de programa educacional


O protocolo HTTP, desenvolvido no início dos anos 90 do século passado, entrou no nosso cotidiano com tanta força que é difícil imaginar pelo menos um dia sem usá-lo. No entanto, por si só, nem sequer fornece um nível mínimo de segurança ao trocar informações entre um usuário e um servidor da web. O HTTPS ("S" - seguro) chega em socorro: usando o empacotamento dos dados transmitidos em SSL / TLS, esse protocolo provou ser um protetor da informação contra interceptação e é promovido ativamente pela indústria.

Por exemplo, o Google aderiu a 2014Posição "HTTPS em todos os lugares" e até reduz a prioridade dos sites sem ela nos resultados da pesquisa. Essa "propaganda" também não é ignorada pelos consumidores comuns: os navegadores modernos alertam seus usuários sobre a presença e a correção dos certificados SSL dos sites visitados.





O custo de um certificado para um site pessoal começa em dezenas de dólares. Nem sempre a compra é justificada e apropriada. Felizmente, desde o final de 2015, uma alternativa gratuita na forma de certificados Let's Encrypt (LE) está disponível . Este projeto sem fins lucrativos foi criado por entusiastas da Mozilla para cobrir a maioria dos sites com criptografia.

A Autoridade de Certificação emite certificados validados por domínio(o mais simples dentre os disponíveis no mercado) com um período de validade de 90 dias e, por muitos anos, foi possível emitir o chamado certificado curinga para vários subdomínios.

Para obter o certificado, o site usa os algoritmos descritos no protocolo ACME ( Automated Certificate Management Environment ), criado especificamente para o Let's Encrypt. Ao usá-lo, a confirmação da propriedade do domínio é realizada por solicitações através da colocação de um código HTTP específico (chamado HTTP-01 ) ou da instalação de registros DNS (DNS-01) - mais sobre eles serão fornecidos abaixo.

Gerente de certificação


O gerenciador de certificações é um projeto especial para o Kubernetes, que é um conjunto de CustomResourceDefinitions (daí a limitação da versão mínima suportada do K8s - v1.12) para configuração da CA (autoridades de certificação) e pedido direto de certificados. A instalação do CRD em um cluster é trivial e se resume ao uso de um arquivo YAML:

kubectl create ns cert-manager
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v0.13.0/cert-manager.yaml

( Também há a possibilidade de instalar usando o Helm.)

Para iniciar o procedimento de pedido, os recursos das autoridades de certificação (CA) devem ser declarados no cluster: Issuerou ClusterIssuer, - que são usados ​​para assinar o CSR (solicitações de emissão de certificado). A diferença entre o primeiro recurso e o segundo está no escopo:

  • Issuer pode ser usado no mesmo espaço para nome,
  • ClusterIssuer é um objeto de cluster global.

Pratique com o gerente de certificação


No. 1. Certificado autoassinado


Vamos começar com o caso mais simples: solicitar um certificado autoassinado. Essa opção é bastante comum, por exemplo, para ambientes de teste dinâmico para desenvolvedores ou no caso de usar um balanceador externo que finalize o tráfego SSL.

O recurso Issuerterá a seguinte aparência:

apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
  name: selfsigned
spec:
  selfSigned: {}

E para emitir um certificado, é necessário descrever o recurso Certificate, onde é indicado como emiti-lo (consulte a seção issuerRefabaixo) e onde secretNameestá localizada a chave privada (campo ). Depois disso, o Ingress precisará consultar esta chave (consulte a seção tlsc spec):

---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: selfsigned-crt
spec:
  secretName: tls-secret
  issuerRef:
    kind: Issuer
    name: selfsigned
  commonName: "yet-another.website"
  dnsNames:
  - "yet-another.website"
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: app
spec:
  tls:
  - hosts:
    - "yet-another.website"
    secretName: tls-secret
  rules:
  - host: "yet-another.website"
    http:
      paths:
      - path: /
        backend:
          serviceName: app
          servicePort: 8080

Alguns segundos após adicionar esses recursos ao cluster, o certificado será emitido. Você pode ver o relatório correspondente na saída do comando:

kubectl -n app describe  certificate selfsigned-crt
...
  Normal  GeneratedKey  5s    cert-manager  Generated a new private key
  Normal  Requested     5s    cert-manager  Created new CertificateRequest resource "selfsigned-crt-4198958557"
  Normal  Issued        5s    cert-manager  Certificate issued successfully

Se você olhar para o próprio recurso secreto, ele conterá:

  • chave privada tls.key,
  • certificado raiz ca.crt,
  • Nosso certificado autoassinado tls.crt.

O conteúdo desses arquivos pode ser visto usando o utilitário openssl, por exemplo, assim:

kubectl -n app get secret tls-secret -ojson | jq -r '.data."tls.crt"' | base64 -d | openssl x509 -dates -noout -issuer
notBefore=Feb 10 21:01:59 2020 GMT
notAfter=May 10 21:01:59 2020 GMT
issuer=O = cert-manager, CN = yet-another.website

É importante notar que, no caso geral, o certificado emitido usando isso não será confiável pelosIssuer clientes conectados . O motivo é simples: ele não possui uma autoridade de certificação (consulte a nota no site do projeto ) . Para evitar isso, é necessário especificar no caminho para o arquivo secreto onde ele está contido . Pode ser uma organização corporativa da CA - para assinar os certificados emitidos para o Ingress com uma chave que já é usada para as necessidades de outros serviços / sistemas de informações do servidor.Certificateca.crt

No. 2. Vamos criptografar certificado com validação HTTP


Para emitir certificados LE, como mencionado anteriormente, dois tipos de confirmação de propriedade do domínio estão disponíveis : HTTP-01 e DNS-01.

A primeira abordagem (HTTP-01) é iniciar um pequeno servidor Web com uma implantação separada, que enviará para a Internet pelo link <YOUR_DOMAIN > / .Bem conhecido / acme-challenge / <TOKEN> alguns dados solicitados ao servidor de certificação. Portanto, esse método implica a disponibilidade do Ingress de fora da porta 80 e a resolução do registro DNS do domínio em endereços IP públicos.

A segunda opção para verificar o certificado emitido (DNS-01) vem dede ter uma API no servidor DNS que hospeda os registros de domínio. O emissor, usando os tokens indicados, cria registros TXT no domínio, que o servidor ACME recebe durante a confirmação. Entre os provedores DNS oficialmente suportados, estão CloudFlare, AWS Route53, Google CloudDNS e outros, incluindo sua própria implementação ( acme-dns ).

Nota : Let's Encrypt tem limites bastante rígidos nas solicitações para servidores ACME. Para não entrar em uma proibição longa, é recomendável usar o tipo de certificado letsencrypt-staging para depuração (a diferença está apenas no servidor ACME).

Então, descrevemos os recursos:

apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
  name: letsencrypt
spec:
  acme:
    server: https://acme-staging-v02.api.letsencrypt.org/directory 
    privateKeySecretRef:
      name: letsencrypt
    solvers:
    - http01:
       ingress:
         class: nginx
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: le-crt
spec:
  secretName: tls-secret
  issuerRef:
    kind: Issuer
    name: letsencrypt
  commonName: yet-another.website
  dnsNames:
  - yet-another.website

Observe que o endereço do servidor intermediário está especificado como servery acme(c Issuer) . Substituí-lo por uma batalha será possível mais tarde. Aplicando essa configuração, traçamos todo o caminho do pedido:



  1. A criação Certificategerou um novo recurso CertificateRequest:

    kubectl -n app describe certificate le-crt
    ...
    Created new CertificateRequest resource "le-crt-1127528680"
  2. Em sua descrição - uma marca na criação Order:

    kubectl -n app describe certificaterequests le-crt-1127528680
    Created Order resource app/le-crt-1127528680-1805948596
  3. Ele Orderdescreve com quais parâmetros o teste passa e qual é seu status atual. Essa verificação é realizada pelo recurso Challenge:

    kubectl -n app describe order le-crt-1127528680-1805948596
    Created Challenge resource "le-crt-1127528680-1805948596-1231544594" for domain "yet-another.website"
  4. Por fim, os detalhes deste recurso contêm informações sobre o status da própria verificação:

    kubectl -n app describe challenges le-crt-1127528680-1805948596-1231544594
    ...
      Reason:      Successfully authorized domain                                                                                                                                                                      
    ...
      Normal  Started         2m45s  cert-manager  Challenge scheduled for processing
      Normal  Presented       2m45s  cert-manager  Presented challenge using http-01 challenge mechanism
      Normal  DomainVerified  2m22s  cert-manager  Domain "yet-another.website" verified with "http-01" validation

Se todas as condições forem atendidas (ou seja, o domínio for acessível a partir do exterior, não haverá proibição da LE ...) - em menos de um minuto, o certificado será emitido. Se for bem-sucedido, um describe certificate le-crtregistro aparecerá na saída Certificate issued successfully.

Agora você pode alterar com segurança o endereço do servidor para combat ( https://acme-v02.api.letsencrypt.org/directory) e reordenar os certificados reais já assinados Fake LE Intermediate X1, mas não Let's Encrypt Authority X3.

Para fazer isso, primeiro é necessário excluir o recurso Certificate: caso contrário, nenhum procedimento de pedido será ativado, porque o certificado já existe e é relevante. A remoção de um segredo levará ao seu retorno imediato com uma mensagem para describe certificate:

  Normal  PrivateKeyLost  44s                   cert-manager  Lost private key for CertificateRequest "le-crt-613810377", deleting old resource

Resta aplicar o manifesto de “combate” Issuerao já descrito acima Certificate(não foi alterado):

apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
  name: letsencrypt
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt
    solvers:
    - http01:
       ingress:
         class: nginx

Após receber a mensagem Certificate issued successfully, describeverifique:

kubectl -n app get secret tls-secret -ojson | jq -r '.data."tls.crt"' | base64 -d | openssl x509 -dates -noout -issuer
notBefore=Feb 10 21:11:48 2020 GMT
notAfter=May 10 21:11:48 2020 GMT
issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3

Número 3. Curinga LE com validação de DNS


Vamos complicar a tarefa escrevendo um certificado imediatamente para todos os subdomínios do site e desta vez usando a verificação DNS (via CloudFlare).

Primeiro, obtenha o token no painel de controle do CloudFlare para trabalhar com a API:

  1. Perfil → Tokens de API → Criar Token.
  2. Defina as permissões da seguinte maneira:
    • Permissões:
      • Zona - DNS - Editar
      • Zona - Zona - Leitura
    • Recursos da zona:
      • Incluir - todas as zonas
  3. Copie o token recebido após salvar (por exemplo :) y_JNkgQwkroIsflbbYqYmBooyspN6BskXZpsiH4M.

Crie um segredo no qual esse token será armazenado e consulte-o no emissor:

apiVersion: v1
kind: Secret
metadata:
  name: cloudflare-api-token
type: Opaque
stringData:
  api-token: y_JNkgQwkroIsflbbYqYmBooyspN6BskXZpsiH4M
---
apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
  name: letsencrypt
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory 
    privateKeySecretRef:
      name: letsencrypt
    solvers:
    - dns01:
        cloudflare:
          email: my-cloudflare-acc@example.com
          apiTokenSecretRef:
            name: cloudflare-api-token
            key: api-token

---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: le-crt
spec:
  secretName: tls-secret
  issuerRef:
    kind: Issuer
    name: letsencrypt
  commonName: yet-another.website
  dnsNames:
  - "yet-another.website"
  - "*.yet-another.website"

(Não se esqueça de usar o armazenamento temporário, se você estiver experimentando!)

Vamos seguir o processo de confirmação da propriedade do domínio:

kubectl -n app describe challenges.acme.cert-manager.io le-crt-613810377-1285319347-3806582233
...
Status:
  Presented:   true
  Processing:  true
  Reason:      Waiting for dns-01 challenge propagation: DNS record for "yet-another.website" not yet propagated
  State:       pending
Events:
  Type    Reason     Age   From          Message
  ----    ------     ----  ----          -------
  Normal  Started    54s   cert-manager  Challenge scheduled for processing
  Normal  Presented  53s   cert-manager  Presented challenge using dns-01 challenge mechanism

Um registro TXT aparecerá no painel:



... e depois de um tempo o status mudará para:

Domain "yet-another.website" verified with "dns-01" validation

Verifique se o certificado é válido para qualquer subdomínio:

kubectl -n app get secret tls-secret -ojson | jq -r '.data."tls.crt"' | base64 -d | openssl x509 -dates -noout -text |grep DNS:
          DNS:*.yet-another.website, DNS:yet-another.website

A validação pelo DNS, por via de regra, não ocorre rapidamente, pois a maioria dos provedores de DNS tem um período de atualização de dados que mostra quanto tempo decorre desde o momento em que o registro DNS é alterado para a atualização real de todos os servidores DNS do provedor. No entanto, o padrão ACME também fornece uma combinação de duas opções de verificação, que podem ser usadas para agilizar o recebimento de um certificado para o domínio principal. Nesse caso, a descrição Issuerserá a seguinte:

apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
  name: letsencrypt
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt
    solvers:
    - selector:
        dnsNames:
        - "*.yet-another.website"
      dns01:
        cloudflare:
          email: my-cloudflare-acc@example.com
          apiTokenSecretRef:
            name: cloudflare-api-token
            key: api-token            
    - selector:
        dnsNames:
        - "yet-another.website"
      http01:
        ingress:
          class: nginx

Se você aplicar essa configuração, dois recursos serão criados Challenge:

kubectl -n app describe orders le-crt-613810377-1285319347
  Normal  Created  3m29s  cert-manager  Created Challenge resource "le-crt-613810377-1285319347-3996324737" for domain "yet-another.website"                 
  Normal  Created  3m29s  cert-manager  Created Challenge resource "le-crt-613810377-1285319347-1443470517" for domain "yet-another.website"

Número 4. Usando anotações especiais do Ingress


Além do caminho direto para a criação de certificados no gerenciador de certificados, é possível usar um componente chamado ingress-shim e não criar recursos explicitamente Certificate. A idéia é que, com a ajuda de anotações especiais do Ingress, o certificado seja pedido automaticamente usando o indicado nelas Issuer. O resultado é aproximadamente o seguinte recurso do Ingress:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt
spec:
  tls:
  - hosts:
    - "yet-another.website"
    secretName: tls-secret
  rules:
  - host: "yet-another.website"
    http:
      paths:
      - path: /
        backend:
          serviceName: app
          servicePort: 8080

Para uma operação correta, apenas a presença do Emissor é suficiente aqui, ou seja, criando uma entidade a menos.

Além disso, há uma anotação obsoleta kube-lego - kubernetes.io/tls-acme: "true", - que requer uma tarefa Issuerpadrão ao instalar o gerenciador de certificados usando os parâmetros Helm (ou nas opções de ativação do contêiner do gerenciador).

Na empresa, não usamos essas opções e não podemos aconselhá-las devido à opacidade das abordagens usadas para solicitar certificados SSL (e ao mesmo tempo a vários problemas que surgem ), mas ainda assim decidimos mencionar no artigo para obter uma imagem mais completa.

Em vez de uma conclusão


Por meio de manipulações simples com o CRD, aprendemos a escrever certificados SSL auto-renováveis, autoassinados e gratuitos no projeto Let's Encrypt para sites de domínio lançados como parte dos agrupamentos do Ingresss in Kubernetes.

O artigo fornece exemplos de solução dos problemas mais comuns em nossa prática. No entanto, as funções de gerente de certificação não se limitam aos recursos descritos acima. No site do utilitário, você pode encontrar exemplos de como trabalhar com outros serviços - por exemplo, um pacote com o Vault ou o uso de centros de emissão externos (emissores).

PS


Leia também no nosso blog:


All Articles