Certificados SSL/TLS válidos são um requisito fundamental do cenário de aplicativos modernos. Infelizmente, gerenciar renovações de certificados (ou cert) geralmente é uma reflexão tardia ao implantar um aplicativo. Os certificados têm uma vida útil limitada, variando de aproximadamente 13 meses para certificados da DigiCert a 90 dias para certificados da Let's Encrypt . Para manter o acesso seguro, esses certificados precisam ser renovados/reemitidos antes de expirarem. Dada a carga de trabalho substancial da maioria das equipes de operações, a renovação de certificados às vezes não é realizada, resultando em confusão à medida que os certificados se aproximam — ou pior, passam — da data de expiração.
Não precisa ser assim. Com algum planejamento e preparação, o gerenciamento de certificados pode ser automatizado e simplificado. Aqui, veremos uma solução para o Kubernetes usando três tecnologias:
Neste blog, você aprenderá a simplificar o gerenciamento de certificados fornecendo certificados exclusivos, renovados e atualizados automaticamente para seus endpoints.
Antes de entrarmos em detalhes técnicos, precisamos definir alguma terminologia. O termo “certificado TLS” se refere a dois componentes necessários para habilitar conexões HTTPS em nosso controlador Ingress:
Tanto o certificado quanto a chave privada são emitidos pela Let's Encrypt . Para uma explicação completa de como os certificados TLS funcionam, consulte a postagem da DigiCert Como funcionam os certificados TLS/SSL .
No Kubernetes, esses dois componentes são armazenados como Secrets . Cargas de trabalho do Kubernetes – como o NGINX Ingress Controller e o cert-manager – podem gravar e ler esses segredos, que também podem ser gerenciados por usuários que têm acesso à instalação do Kubernetes.
O projeto cert-manager é um controlador de certificado que funciona com Kubernetes e OpenShift. Quando implantado no Kubernetes, o cert-manager emitirá automaticamente os certificados exigidos pelos controladores do Ingress e garantirá que eles sejam válidos e atualizados. Além disso, ele rastreará as datas de expiração dos certificados e tentará renová-los em um intervalo de tempo configurado. Embora trabalhe com vários emissores públicos e privados, mostraremos sua integração com o Let's Encrypt.
Ao usar o Let’s Encrypt, todo o gerenciamento de certificados é feito automaticamente. Embora isso ofereça muita conveniência, também apresenta um problema: Como o serviço garante que você é o proprietário do nome de domínio totalmente qualificado (FQDN) em questão?
Esse problema é resolvido usando um desafio , que exige que você responda a uma solicitação de verificação que somente alguém com acesso aos registros DNS do domínio específico pode fornecer. Os desafios assumem uma de duas formas:
HTTP-01 é a maneira mais simples de gerar um certificado, pois não requer acesso direto ao provedor de DNS. Esse tipo de desafio é sempre conduzido pela Porta 80 (HTTP). Observe que ao usar desafios HTTP-01, o cert-manager utilizará o controlador Ingress para servir o token de desafio.
Um controlador de entrada é um serviço especializado para Kubernetes que traz tráfego de fora do cluster, equilibra a carga para Pods internos (um grupo de um ou mais contêineres) e gerencia o tráfego de saída. Além disso, o controlador Ingress é controlado pela API do Kubernetes e monitorará e atualizará a configuração de balanceamento de carga conforme os Pods são adicionados, removidos ou falham.
Para saber mais sobre os controladores Ingress, leia os seguintes blogs:
Nos exemplos abaixo, usaremos o NGINX Ingress Controller desenvolvido e mantido pela F5 NGINX.
Esses exemplos pressupõem que você tenha uma instalação funcional do Kubernetes que pode ser testada e que a instalação pode atribuir um endereço IP externo (objeto Kubernetes LoadBalancer). Além disso, ele pressupõe que você pode receber tráfego na Porta 80 e na Porta 443 (se estiver usando o desafio HTTP-01) ou somente na Porta 443 (se estiver usando o desafio DNS-01). Esses exemplos são ilustrados usando Mac OS X, mas também podem ser usados no Linux ou WSL.
Você também precisará de um provedor de DNS e um FQDN para os quais possa ajustar o registro A. Se você estiver usando o desafio HTTP-01, você só precisa adicionar um registro A (ou ter um adicionado para você). Se estiver usando o desafio DNS-01, você precisará de acesso à API para um provedor de DNS ou um provedor de webhook compatível .
A maneira mais fácil é implantar via Helm . Esta implantação permite que você use o Kubernetes Ingress e o NGINX Virtual Server CRD.
$ helm repo add nginx-stable https://helm.nginx.com/stable "nginx-stable" has been added to your repositories
$ helm repo update Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "nginx-stable" chart repository
Update Complete. ⎈Happy Helming!⎈
$ helm install nginx-kic nginx-stable/nginx-ingress \ --namespace nginx-ingress --set controller.enableCustomResources=true \
--create-namespace --set controller.enableCertManager=true
NAME: nginx-kic
LAST DEPLOYED: Thu Sep 1 15:58:15 2022
NAMESPACE: nginx-ingress
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The NGINX Ingress Controller has been installed.
$ kubectl get deployments --namespace nginx-ingress NAME READY UP-TO-DATE AVAILABLE AGE
nginx-kic-nginx-ingress 1/1 1 1 23s
$ kubectl get services --namespace nginx-ingress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-kic-nginx-ingress LoadBalancer 10.128.60.190 www.xxx.yyy.zzz 80:31526/TCP,443:32058/TCP 30s
O processo aqui dependerá do seu provedor de DNS. Esse nome DNS precisará ser resolvido pelos servidores Let's Encrypt, o que pode exigir que você espere o registro se propagar antes que ele funcione. Para mais informações sobre isso, consulte o artigo do SiteGround O que é propagação de DNS e por que demora tanto?
Depois de resolver o FQDN escolhido, você estará pronto para passar para a próxima etapa.
$ host cert.example.com cert.example.com has address www.xxx.yyy.zzz
O próximo passo é implantar a versão mais recente do cert-manager. Novamente, usaremos o Helm para nossa implantação.
$ helm repo add jetstack https://charts.jetstack.io "jetstack" has been added to your repositories
$ helm repo update Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "nginx-stable" chart repository
...Successfully got an update from the "jetstack" chart repository
Update Complete. ⎈Happy Helming!⎈
$ helm install cert-manager jetstack/cert-manager \ --namespace cert-manager --create-namespace \
--version v1.9.1 --set installCRDs=true
NAME: cert-manager
LAST DEPLOYED: Thu Sep 1 16:01:52 2022
NAMESPACE: cert-manager
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
cert-manager v1.9.1 has been deployed successfully!
In order to begin issuing certificates, you will need to set up a ClusterIssuer
or Issuer resource (for example, by creating a 'letsencrypt-staging' issuer).
More information on the different types of issuers and how to configure them
can be found in our documentation:
https://cert-manager.io/docs/configuration/
For information on how to configure cert-manager to automatically provision
Certificates for Ingress resources, take a look at the `ingress-shim`
documentation:
https://cert-manager.io/docs/usage/ingress/
$ kubectl get deployments --namespace cert-manager NAME READY UP-TO-DATE AVAILABLE AGE
cert-manager 1/1 1 1 4m30s
cert-manager-cainjector 1/1 1 1 4m30s
cert-manager-webhook 1/1 1 1 4m30s
Usaremos o exemplo do NGINX Cafe para fornecer nossa implantação de backend e serviços. Este é um exemplo comum usado na documentação fornecida pelo NGINX. Não implementaremos o Ingress como parte disso.
$ git clone https://github.com/nginxinc/kubernetes-ingress.git Cloning into 'kubernetes-ingress'...
remote: Enumerating objects: 44979, done.
remote: Counting objects: 100% (172/172), done.
remote: Compressing objects: 100% (108/108), done.
remote: Total 44979 (delta 87), reused 120 (delta 63), pack-reused 44807
Receiving objects: 100% (44979/44979), 60.27 MiB | 27.33 MiB/s, done.
Resolving deltas: 100% (26508/26508), done.
$ cd ./kubernetes-ingress/examples/ingress-resources/complete-example
$ kubectl apply -f ./cafe.yaml
deployment.apps/coffee created
service/coffee-svc created
deployment.apps/tea created
service/tea-svc created
kubectl
get. Você quer garantir que os Pods estejam aparecendo como PRONTOS
e os Serviços estejam aparecendo como em execução
. O exemplo abaixo mostra uma amostra representativa do que você está procurando. Observe que o serviço kubernetes
é um serviço de sistema executado no mesmo namespace (padrão) do exemplo do NGINX Cafe.$ kubectl get deployments,services --namespace default NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/coffee 2/2 2 2 69s
deployment.apps/tea 3/3 3 3 68s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/coffee-svc ClusterIP 10.128.154.225 <none> 80/TCP 68s
service/kubernetes ClusterIP 10.128.0.1 <none> 443/TCP 29m
service/tea-svc ClusterIP 10.128.96.145 <none> 80/TCP 68s
Dentro do cert-manager, o ClusterIssuer pode ser usado para emitir certificados. Este é um objeto com escopo de cluster que pode ser referenciado por qualquer namespace e usado por qualquer solicitação de certificado com a autoridade emissora de certificado definida. Neste exemplo, qualquer solicitação de certificado para certificados Let's Encrypt pode ser manipulada por este ClusterIssuer.
Implante o ClusterIssuer para o tipo de desafio que você selecionou. Embora esteja fora do escopo desta postagem, há opções de configuração avançadas que permitem especificar vários resolvedores (escolhidos com base nos campos do seletor) no seu ClusterIssuer.
O protocolo Automated Certificate Management Environment (ACME) é usado para determinar se você possui um nome de domínio e, portanto, pode receber um certificado Let’s Encrypt. Para este desafio, estes são os parâmetros que precisam ser passados:
Este exemplo mostra como configurar um ClusterIssuer para usar o desafio HTTP-01 para provar a propriedade do domínio e receber um certificado.
$ cat << EOF | kubectl apply -f apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: prod-issuer
spec:
acme:
email: example@example.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: prod-issuer-account-key
solvers:
- http01:
ingress:
class: nginx
EOF
clusterissuer.cert-manager.io/prod-issuer created
$ kubectl get clusterissuer NAME READY AGE
prod-issuer True 34s
Este exemplo mostra como configurar um ClusterIssuer para usar o desafio DNS-01 para autenticar a propriedade do seu domínio. Dependendo do seu provedor de DNS, você provavelmente precisará usar um Kubernetes Secret para armazenar seu token. Este exemplo usa o Cloudflare . Observe o uso do namespace. O aplicativo cert-manager, que é implantado no namespace cert-manager, precisa ter acesso ao Secret .
Para este exemplo, você precisará de um token da API do Cloudflare , que pode ser criado na sua conta. Isso precisará ser colocado na linha abaixo. Se você não estiver usando o Cloudflare, precisará seguir a documentação do seu provedor .
$ cat << EOF | kubectl apply -f apiVersion: v1
kind: Secret
metadata:
name: cloudflare-api-token-secret
namespace: cert-manager
type: Opaque
stringData:
api-token: <API Token>
EOF
$ cat << EOF | kubectl apply -f apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: prod-issuer
spec:
acme:
email: example@example.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: prod-issuer-account-key
solvers:
- dns01:
cloudflare:
apiTokenSecretRef:
name: cloudflare-api-token-secret
key: api-token
EOF
$ kubectl get clusterissuer NAME READY AGE
prod-issuer True 31m
Este é o ponto em que estamos construindo – a implantação do recurso Ingress para nosso aplicativo. Isso roteará o tráfego para o aplicativo NGINX Cafe que implantamos anteriormente.
Se estiver usando o recurso Ingress padrão do Kubernetes, você usará o seguinte YAML de implantação para configurar o Ingress e solicitar um certificado.
apiVersion: networking.k8s.io/v1 kind: Ingress
metadata:
name: cafe-ingress
annotations:
cert-manager.io/cluster-issuer: prod-issuer
acme.cert-manager.io/http01-edit-in-place: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- cert.example.com
secretName: cafe-secret
rules:
- host: cert.example.com
http:
paths:
- path: /tea
pathType: Prefix
backend:
service:
name: tea-svc
port:
number: 80
- path: /coffee
pathType: Prefix
backend:
service:
name: coffee-svc
port:
number: 80
Vale a pena rever algumas partes importantes do manifesto:
metadata.annotations
, onde definimos acme.cert-manager.io/http01-edit-in-place
como “true”. Este valor é obrigatório e ajusta a maneira como o desafio é atendido. Para mais informações, consulte o documento Anotações Suportadas . Isso também pode ser resolvido usando uma configuração mestre/servo .spec.ingressClassName
se refere ao controlador NGINX Ingress que instalamos e usaremos.spec.tls.secret
do Kubernetes Secret armazena a chave de certificado que é retornada quando o certificado é emitido pelo Let's Encrypt. cert.example.com
é especificado para spec.tls.hosts
e spec.rules.host
. Este é o nome do host para o qual nosso ClusterIssuer emitiu o certificado.spec.rules.http
define os caminhos e os serviços de backend que atenderão às solicitações nesses caminhos. Por exemplo, o tráfego para /tea
será direcionado para a porta 80 no tea-svc
.spec.rules.host
e spec.tls.hosts
, mas você deve revisar todos os parâmetros na configuração. $ kubectl apply -f ./cafe-virtual-server.yaml virtualserver.k8s.nginx.org/cafe created
$ kubectl get certificates NAME READY SECRET AGE
certificate.cert-manager.io/cafe-secret True cafe-secret 37m
Se estiver usando os CRDs do NGINX, você precisará usar o seguinte YAML de implantação para configurar seu Ingress.
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
name: cafe
spec:
host: cert.example.com
tls:
secret: cafe-secret
cert-manager:
cluster-issuer: prod-issuer
upstreams:
- name: tea
service: tea-svc
port: 80
- name: coffee
service: coffee-svc
port: 80
routes:
- path: /tea
action:
pass: tea
- path: /coffee
action:
pass: coffee
Mais uma vez, vale a pena rever algumas partes importantes do manifesto:
spec.tls.secret
do Kubernetes Secret armazena a chave de certificado que é retornada quando o certificado é emitido pelo Let's Encrypt. cert.example.com
é especificado para spec.host
. Este é o nome do host para o qual nosso ClusterIssuer emitiu o certificado.spec.upstreams
apontam para nossos serviços de backend, incluindo as portas.spec.routes
define a rota e a ação a ser tomada quando essas rotas são atingidas.spec.host
, mas você deve revisar todos os parâmetros na configuração. $ kubectl apply -f ./cafe-virtual-server.yaml virtualserver.k8s.nginx.org/cafe created
$ kubectl get VirtualServers NAME STATE HOST IP PORTS AGE
cafe Valid cert.example.com www.xxx.yyy.zzz [80,443] 51m
Você pode visualizar o certificado por meio da API do Kubernetes. Isso mostrará detalhes sobre o certificado, incluindo seu tamanho e a chave privada associada.
$ kubectl describe secret cafe-secret Name: cafe-secret
Namespace: default
Labels: <none>
Annotations: cert-manager.io/alt-names: cert.example.com
cert-manager.io/certificate-name: cafe-secret
cert-manager.io/common-name: cert.example.com
cert-manager.io/ip-sans:
cert-manager.io/issuer-group:
cert-manager.io/issuer-kind: ClusterIssuer
cert-manager.io/issuer-name: prod-issuer
cert-manager.io/uri-sans:Type: kubernetes.io/tlsData
====
tls.crt: 5607 bytes
tls.key: 1675 bytes
Se você quiser ver o certificado e a chave reais, pode fazê-lo executando o seguinte comando. (Observação: Isso ilustra uma fraqueza dos segredos do Kubernetes. Ou seja, eles podem ser lidos por qualquer pessoa com as permissões de acesso necessárias.)
$ kubectl get secret cafe-secret -o yaml
Teste os certificados. Você pode usar qualquer método que desejar aqui. O exemplo abaixo usa cURL . O sucesso é indicado por um bloco similar ao que é mostrado antes, que inclui o nome do servidor, endereço interno do servidor, data, o URI (rota) escolhido (café ou chá) e o ID da solicitação. Falhas tomarão a forma de códigos de erro HTTP, provavelmente 400 ou 301.
$ curl https://cert.example.com/tea
Server address: 10.2.0.6:8080
Server name: tea-5c457db9-l4pvq
Date: 02/Sep/2022:15:21:06 +0000
URI: /tea
Request ID: d736db9f696423c6212ffc70cd7ebecf
$ curl https://cert.example.com/coffee
Server address: 10.2.2.6:8080
Server name: coffee-7c86d7d67c-kjddk
Date: 02/Sep/2022:15:21:10 +0000
URI: /coffee
Request ID: 4ea3aa1c87d2f1d80a706dde91f31d54
No início, prometemos que essa abordagem eliminaria a necessidade de gerenciar renovações de certificados. No entanto, ainda precisamos explicar como fazer isso. Porquê?Porque esta é uma parte central e integrada do cert-manager. Neste processo automático, quando o cert-manager percebe que um certificado não está presente, expirou, está a 15 dias do vencimento ou se o usuário solicita um novo certificado via CLI, um novo certificado é solicitado automaticamente . Não fica muito mais fácil do que isso.
Se você for assinante do NGINX Plus, a única diferença para você envolverá a instalação do NGINX Ingress Controller. Consulte a seção Instalação do Helm da documentação do NGINX para obter instruções sobre como modificar o comando Helm fornecido acima para fazer isso.
Isso depende muito do seu caso de uso.
O método de desafio HTTP-01 requer que a porta 80 esteja aberta para a Internet e que o registro DNS A tenha sido configurado corretamente para o endereço IP do controlador Ingress. Essa abordagem não requer acesso ao provedor de DNS além da criação do registro A.
O método de desafio DNS-01 pode ser usado quando você não pode expor a Porta 80 à Internet e requer apenas que o gerenciador de certificados tenha acesso de saída ao provedor de DNS. No entanto, esse método exige que você tenha acesso à API do seu provedor de DNS, embora o nível de acesso necessário varie de acordo com o provedor específico.
Como o Kubernetes é tão complexo, é difícil fornecer informações específicas para solução de problemas. Caso você tenha algum problema, gostaríamos de convidá-lo a nos perguntar no NGINX Community Slack (assinantes do NGINX Plus podem usar suas opções normais de suporte).
Comece solicitando sua avaliação gratuita de 30 dias do NGINX Ingress Controller com NGINX App Protect WAF e DoS e baixe o NGINX Service Mesh sempre gratuito.
"Esta postagem do blog pode fazer referência a produtos que não estão mais disponíveis e/ou não têm mais suporte. Para obter as informações mais atualizadas sobre os produtos e soluções F5 NGINX disponíveis, explore nossa família de produtos NGINX . O NGINX agora faz parte do F5. Todos os links anteriores do NGINX.com redirecionarão para conteúdo semelhante do NGINX no F5.com."