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: Issuer
ou 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 Issuer
terá 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 issuerRef
abaixo) e onde secretName
está localizada a chave privada (campo ). Depois disso, o Ingress precisará consultar esta chave (consulte a seção tls
c 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.Certificate
ca.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 server
y acme
(c Issuer
) . Substituí-lo por uma batalha será possível mais tarde. Aplicando essa configuração, traçamos todo o caminho do pedido:- A criação
Certificate
gerou um novo recurso CertificateRequest
:
kubectl -n app describe certificate le-crt
...
Created new CertificateRequest resource "le-crt-1127528680"
- 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
- Ele
Order
descreve 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"
- 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-crt
registro 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” Issuer
ao 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
, describe
verifique: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:- Perfil → Tokens de API → Criar Token.
- Defina as permissões da seguinte maneira:
- Permissões:
- Zona - DNS - Editar
- Zona - Zona - Leitura
- Recursos da zona:
- 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 Issuer
será a seguinte:apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: letsencrypt
spec:
acme:
server: https:
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 Issuer
padrã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: