kubernetes + cert-manager + let's encrypt

Criando Ingress com SSL válidos para suas aplicações em kubernetes

Dependências

  • helm

  • kubectl

  • Aplicação já funcionando e com ingress configurado para http.


Objetivo

O objetivo desse tutorial é criar documentação em PT-BR e ajudar a quem esta iniciando suas primeiras configurações com kubernetes.

Sei exatamente como tudo fica mais difícil quando não temos um ingles de qualidade. Por isso, temos que correr atrás. Temos que aprender.

Este tutorial não vai explicar as diferenças entre versões da API do lets-encrypt, gerações de certificados wildcard, ou sobre como validar os certificados via DNS.

A leitura da documentação oficial é indispensável.

Introdução

O cert-manager é uma ferramenta que foi desenvolvida pela empresa Jetstack, e que por sinal, tem outros projetos interessantes.

Apesar do kubernetes suportar o cert-manager, e ter total compatibilidade com seus "kinds", o cert-manager não faz parte (até hoje: Wed Sep 19 20:27:09 -03 2018) da lista de módulos Kubernetes Incubator.

A leitura do manual do cert-manager é obrigatória. O manual é pequeno e a leitura cheia de informações e detalhes importantes para o entendimento dessa maravilhosa aplicação que revolucionou e ajudou muito na padronização do SSL na internet.

 

Configuração

Poderíamos criar todos os kinds do cert-manager dentro do namespace kube-system, mas para uma melhor organização vamos criar um namespace parar ele.

O cert-manager em kubernetes não utiliza dados persistentes, pois sua configuração é criada e referenciada por seus kinds e pelos ingress das aplicações.


kubectl create ns cert-manager
namespace/cert-manager created
helm install \ --name cert-manager \ --namespace cert-manager \ stable/cert-manager

NAME: cert-manager
LAST DEPLOYED: Wed Sep 19 20:39:55 2018
NAMESPACE: cert-manager 
STATUS: DEPLOYED

RESOURCES:
==> v1beta1/Deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
cert-manager 1 1 1 0 0s

==> v1/Pod(related)
NAME READY STATUS RESTARTS AGE
cert-manager-756d6d885d-fqddf 0/1 ContainerCreating 0 0s

==> v1/ServiceAccount
NAME SECRETS AGE
cert-manager 1 1s

==> v1beta1/CustomResourceDefinition
NAME AGE
certificates.certmanager.k8s.io 1s
clusterissuers.certmanager.k8s.io 1s
issuers.certmanager.k8s.io 1s

==> v1beta1/ClusterRole
cert-manager 0s

==> v1beta1/ClusterRoleBinding
NAME AGE
cert-manager 0s


NOTES:
cert-manager 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.readthedocs.io/en/latest/reference/issuers.html

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.readthedocs.io/en/latest/reference/ingress-shim.html


Como já sabemos pela leitura da documentação, existem dois tipos de Issuers.

O Issuer, que somente vai validar as os certificados que estiverem no mesmo namespace, e o ClusterIssuer, que consegue se comunicar/consultar com/os certificados de qualquer namespace.

Em nosso caso, necessitamos que o cert-manager se comunique com todos os namespaces, pois vamos ter aplicações expostas a internet em diferentes namespaces.


Gerando o ClusterIssuer

O clusterissuer é o cara!

Ele vai viver dentro do namespace cert-manager, e vai se dividir em dois, o clusterissuer letsencrypt-staging, e o letsencrypt-prod.

O arquivos são praticamente iguais. Entao crie uma pasta chamada cert-manager e lá dentro vamos gerar os arquivos:

clusterissuer-staging.yaml

$ cat ./k8s/cert-manager/clusterissuer-staging.yaml 
apiVersion: certmanager.k8s.io/v1alpha1 kind: ClusterIssuer metadata:
name: letsencrypt-staging
namespace: cert-manager
spec:
acme:
## The ACME server URL
server: https://acme-staging-v02.api.letsencrypt.org/directory
# Email address used for ACME registration
email: coloque-aqui-seu-email@dominio.com
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: letsencrypt-staging
# Enable HTTP01 validations
http01: {}

clusterissuer-prod.yaml

$ cat /home/nemmeviu/Documents/blacktourmaline/k8s/cert-manager/clusterissuer-prod.yaml 
apiVersion: certmanager.k8s.io/v1alpha1 kind: ClusterIssuer metadata:
name: letsencrypt-prod
namespace: cert-manager
spec:
acme:
# The ACME server URL
server: https://acme-v02.api.letsencrypt.org/directory
# Email address used for ACME registration
email: coloque-aqui-o-mesmo-email@o-mesmo-dominio.com
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: letsencrypt-prod
# Enable HTTP01 validations
http01: {}


E logo, para criar esses kinds:

$ kubectl apply -f k8s/cert-manager/


Estamos quase.

Abra outro terminal e visualize os logs de seu pod cert-manager:

$ kubectl -n cert-manager get pod
NAME READY STATUS RESTARTS AGE
cert-manager-756d6d885d-z2srx 1/1 Running 0 1h
$ kubectl -n cert-manager logs -f cert-manager-756d6d885d-z2srx


Valide como foi a criação do pod. Caso tenha algum erro nas mensagens, verifique se aplicou os passos anteriores corretamente.


Criando o ingress

Agora vamos focar na aplicação.

Sabemos que em kubernetes, na maiorias dos casos:

  1. Os usuarios/boots entram pelo ingress

  2. O ingress faz um apontamento para um service

  3. O service faz math das labels de seu selector e assim encontra um pod.

Legal. O cert-manager e bem inteligente, e de todo o fluxo temos somente que agregar algumas linhas no yaml do ingress.

Seu ingress deve ficar assim:

$ cat app-wp-ingress.yaml 
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: wp-app
namespace: meu-namespace
annotations:
ingress.kubernetes.io/ssl-redirect: "true"
kubernetes.io/tls-acme: "true"
certmanager.k8s.io/cluster-issuer: letsencrypt-staging
kubernetes.io/ingress.class: "nginx"
labels:
name: wp-app
app: wordpress-app
area: dev
role: app
version: "1"
environment: wordpress
spec:
tls:
- hosts:
- blog.wordpress.com.br
secretName: wp-app-cert
rules:
- host: blog.wordpress.com.br
http:
paths:
- path: /
backend:
serviceName: wp-app
servicePort: 80


Entao, remova o ingress atual, aplique o novo.

$ kubectl -n meu-namespace delete ingress wp-app
$ kubectl apply -f app-wp-ingress.yaml


Agora corra para os logs que estavam no outro terminal, e verifique se tudo aconteceu bem.

Caso tudo tenha acontecido bem, corra para seu browser e verifique se existe algum certificado "fake" criado.

É importante realizar todas as provas em ambiente staging, porque a API produtiva do lets-encrypt tem muitos bloqueios, logo, se começar a fazer perguntas lá, sem ter a configuração OK, vai tomar um DROP por algumas horas.

Se conseguir gerar o certificado "fake", remova o ingress novamente:

$ kubectl -n meu-namespace delete ingress wp-app

E entao remova tambem os certificados e secrets:

$ kubectl -n meu-namespace get secret 
NAME TYPE DATA AGE
default-token-4vpcp kubernetes.io/service-account-token 3 17d
wp-app-cert kubernetes.io/tls 2 1h
$ kubectl -n meu-namespace get certificate
NAME CREATED AT
wp-app-cert 1h
$
$ kubectl -n meu-namespace delete secret wp-app-cert
$ kubectl -n meu-namespace delete certificate wp-app-cert


Feito isso, altere o arquivo do ingress, e troque de staging para prod, e então:

$ kubectl apply -f app-wp-ingress.yaml