HTTPs no Kubernetes com Traefik e CertManager

Veja como configurar suas aplicações java para ter HTTPs utilizando Traefik e CertManager

Capa

 

Introdução

 

Nesse post, irei mostrar como disponibilizar um blog com acesso HTTPS em um servidor utilizando Kubernetes, Traefik e CertManager.

 

Instalando o Kubernetes

 

Primeiramente precisamos ter o Kubernetes instalado no servidor. Uma boa opção é utilizar o k3s, uma distribuição do kubernetes bastante leve e eficiente.

 

Antes de inciar a instalação, é recomendado atualizar os pacotes e instalar o curl:

 

sudo apt update && sudo apt upgrade -y
sudo apt install -y curl

 

O k3s vem com uma versão do Traefik instalada por padrão que não permite algumas configurações necessárias, por isso, precisamos especificar que não queremos ele:

 

sudo mkdir -p /etc/rancher/k3s
sudo tee /etc/rancher/k3s/config.yaml > /dev/null <<'EOF'
disable:
	traefik
EOF

 

Agora sim podemos fazer a instalação:

 

curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--write-kubeconfig-mode 644" sh -

 

Para verificar se ele foi instalado corretamente, rode o comando:

 

sudo systemctl status k3s --no-pager

 

Para poder executar os comandos do kubernetes usando o kubectl sem o sudo, podemos configurar para o usuário:

 

mkdir -p ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $USER:$USER ~/.kube/config

 

Configurando acesso remoto ao Kubernetes

 

Com o kubernetes instalado, já podemos rodar os manifestos para criar os nossos deployments, services, etc. Porém, teríamos que ter esses manifestos dentro do servidor, o que não é muito prático. Para poder executar os manifestos de fora do servidor (de forma remota), basta ajustar o arquivo de configuração do kubernetes da máquina onde você quer executar os comandos remotamente.

 

Primeiro, precisamos pegar o arquivo de configuração do k3s que está dentro do servidor na pasta /etc/rancher/k3s/k3s.yaml. Ele vai ter um conteúdo mais ou menos assim:

 

apiVersion: v1
clusters:
- cluster:
   certificate-authority-data: LS0tLS1CRU... (um bloco grande em base64)
   server: https://127.0.0.1:6443
 name: default

 

Precisamos copiar esses trechos de código dentro do arquivo ~/.kube/config (no windows: C:\Users\<usuario>\.kube\config). 

 

Atenção: não substitua o conteúdo do seu arquivo de configuração com o que está no servidor, e sim adicione os trechos que estão no servidor nos respectivos trechos que estão no arquivo de configuração da sua máquina.

 

Rodando os manifestos remotamente

 

Agora iremos rodar os manifestos de deployment e service da nossa aplicação diretamente no kubernetes dentro do servidor.

 

Primeiro, como boa prática, vamos rodar o namespace.yaml:

 

apiVersion: v1
kind: Namespace
metadata:
 name: blog

 

Em seguida, vamos rodar o deployment.yaml, responsável por criar o POD onde a aplicação vai ser executada:

 

apiVersion: apps/v1
kind: Deployment
metadata:
  name: blog-app-deployment
  namespace: blog
spec:
  replicas: 1
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 1 
  selector:
    matchLabels:
      app: blog-app
  template:
    metadata:
      labels:
        app: blog-app
    spec:
      containers:
        - name: blog-app
          image: [suaimagem]:[versao]
          ports:
            - containerPort: 8080

 

Agora vamos rodar o service.yml, que irá permitir que o POD seja acessado internamente pelo Ingress Traefik:

 

apiVersion: v1
kind: Service
metadata:
 name: blog-app
 namespace: blog
spec:
 type: ClusterIP
 selector:
   app: blog-app
 ports:
   - protocol: TCP
     port: 8080
     targetPort: 8080
     nodePort: 31001

 

Instalando o Helm

 

O Helm é uma ferramenta que ajuda a gerenciar as aplicações no kubernetes. Nesse momento, ele vai nos ajudar a instalar o Traefik e o cert-manager.

 

Para instalá-lo, basta rodar o comando:

 

curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

 

Para verificar se ele foi instalado com sucesso:

 

helm version

 

Instalando o Traefik

 

Agora vamos instalar o Traefik. Ele vai funcionar como um proxy reverso, redirecionando as requisições para os devidos pods. Ele é muito útil pois, a partir de um único domínio, podemos configurar diversos subdomínios. Ou seja, supondo que temos o domínio seudominio.com, podemos ter blog.seudominio.com, forum.seudominio.com, etc.

 

Para instalá-lo utilizando o Helm, primeiro precisamos adicionar o repositório dele:

 

helm repo add traefik https://traefik.github.io/charts
helm repo update

 

Vamos criar um arquivo de configuração na pasta ~/kubernets-confs para realizar a instalação do Traefik chamado traefik-values.yaml. Nesse arquivo, vemos expor a porta web por padrão, além de habilitar o dashboard do Traefik, uma ferramenta que ajuda a monitorar o funcionamento da ferramenta:

 

# Configuração de portas do Traefik
ports:
 web:
   expose:
     default: true  # Expõe a porta web por padrão
   entryPoints:
     - web  # Define o entrypoint HTTP padrão
# Configuração do IngressRoute para o dashboard do Traefik
ingressRoute:
 dashboard:
   enabled: true  # Habilita o dashboard do Traefik
   entryPoints: [web, websecure]  # Permite acesso via HTTP e HTTPS
   matchRule: Host(`traefik-dashboard.lucasferraz.blog`)  # Domínio para acessar o dashboard
   middlewares:
     - name: dashboard-auth  # Aplica autenticação básica ao dashboard

 

Por segurança, adicionamos uma autenticação básica no dashboard. Para que isso funcione, precisamos criar também um manifesto para definir esse mecanismo de autenticação:

 

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
 name: dashboard-auth
 namespace: traefik
spec:
 basicAuth:
   secret: dashboard-auth-secret
---
apiVersion: v1
kind: Secret
metadata:
 name: dashboard-auth-secret
 namespace: traefik
type: Opaque
data:
 users: YWRtaW46JGFwcjEkQ0ZNallHT3kkVUF1OGlhN0VvWkpoM0Q5bXJDdThULw== # admin:admin

 

Em seguida, rodar o comando de instalação:

 

helm install traefik traefik/traefik -n traefik --create-namespace --set service.type=LoadBalancer -f ~/kubernets-confs/traefik-values.yaml

 

Para verificar se a instalação funcionou:

 

kubectl get pods -n traefik

 

Configurando o Ingress e IngressRoute

 

Agora vamos criar os manifestos do Ingress e do IngressRoute para verificar se o Traefik está funcionando realmente.

 

ingress.yml:

 

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
 name: blog-ingress
 namespace: blog
 annotations:
   traefik.ingress.kubernetes.io/router.entrypoints: websecure
spec:
 ingressClassName: traefik
 rules:
   - host: blog.lucasferraz.blog
     http:
       paths:
         - path: /
           pathType: Prefix
           backend:
             service:
               name: blog-app
               port:
                 number: 8080

 

ingressroute.yml:

 

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
 name: blog-ingressroute
 namespace: blog
spec:
 entryPoints:
   - websecure
 routes:
   - match: Host(`blog.lucasferraz.cloud`)
     kind: Rule
     services:
       - name: blog-app
         port: 8080

 

Agora já podemos tentar acessar o endereço blog.lucasferraz.com (ou o domínio que você estiver configurando) e verificar se tudo funcionou corretamente. Se sim, você vai ser direcionado para a aplicação, mas com HTTP ao invés de HTTPS.

 

Configurando o domínio na Cloudflare

 

Para que o Lets Encrypt possa garantir o certificado HTTPS, precisamos que o DNS da aplicação esteja sendo gerenciado pela Cloudflare, que possui uma API que será utilizada para validar o TLS. Não se preocupe, pois a Cloudflare oferece esses serviços gratuitamente e a configuração é simples.

 

Para isso, crie uma conta na Cloudflare (caso não tenha), clique em Add > Connect a Domain, em seguida insira o domínio e configure os records. No nosso caso, precisamos adicionar um record to tipo A, onde o nome seja blog e o valor seja o IP do servidor.

 

Após isso, é necessário obter um token de acesso: ícone do perfil > Profile > API Tokens > Create token > Edit Zone DNS > Em zone resources, selecione o DNS > Continue to summary > Confirm.

 

Pronto, agora copie o token gerado pois será utilizado daqui a pouco.

 

Instalando o cert-manager

 

O cert-manager vai ficar responsável por gerenciar os certificados das aplicações rodando no kubernetes, permitindo que elas sejam acessadas via HTTPS ao invés de HTTP.

 

Primeiro adicionamos o repositório do Helm:

 

helm repo add jetstack https://charts.jetstack.io --force-update

 

Agora vamos definir o arquivo de configurações ~/kubernets-confs/certmanager-values.yaml:

 

# Define o namespace onde o cert-manager será instalado
namespace: "cert-manager"
# Configuração dos CRDs (Custom Resource Definitions)
crds:
 enabled: true  # Instala automaticamente os CRDs necessários para o cert-manager

 

Em seguida, instalamos usando o comando:

 

helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --values ~/kubernets-confs/certmanager-values.yaml

 

Para verificar se instalou corretamente:

 

kubectl get pods -n cert-manager

 

Agora vamos configurar o Issuer e o ClusterIssuer.

 

Primeiro, criamos o arquivo certmanager-issuer-secret.yaml:

 

apiVersion: v1
kind: Secret
metadata:
 name: cloudflare-api-token-secret
 namespace: cert-manager
type: Opaque
stringData:
 api-token: [token da Cloudflare]

 

Em seguida, rodamos o certmanager-clusterissuer.yaml:

 

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
 name: cloudflare-clusterissuer
spec:
 acme:
   email: [seu@email]
   server: https://acme-v02.api.letsencrypt.org/directory
   privateKeySecretRef:
     name: cloudflare-clusterissuer-account-key
   solvers:
   - dns01:
       cloudflare:
         apiTokenSecretRef:
           name: cloudflare-api-token-secret
           key: api-token

 

Agora vamos configurar um manifesto para a geração do certificado que será utilizado no IngressRoute.

 

Vamos criar o arquivo certificate.yaml:

 

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
 name: blog-ingressroute-certificate
 namespace: blog
spec:
 secretName: blog-certificate-secret
 issuerRef:
   name: cloudflare-clusterissuer # aqui deve ser o mesmo nome do ClusterIssue definido anteriormente
   kind: ClusterIssuer
 dnsNames:
 - blog.lucasferraz.cloud # ou o domínio que você estiver configurando

 

Agora precisamos ajustar o arquivo do manifesto do IngressRoute para utilizar esse certificado. O arquivo ingressroute.yaml ficará assim:

 

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
 name: blog-ingressroute
 namespace: blog
spec:
 entryPoints:
   - websecure
 routes:
   - match: Host(`blog.lucasferraz.cloud`)
     kind: Rule
     services:
       - name: blog-app
         port: 8080
 tls:
   secretName: blog-certificate-secret # aqui tem que ser o mesmo nome definido no manifesto do certificado

 

Após executar o manifesto, já deve ser possível acessar a URL via HTTPS.

 

Pronto, agora sua aplicação pode ser acessada de forma segura!

 

Referências

 

 → https://www.youtube.com/watch?v=vJweuU6Qrgo

 → https://cert-manager.io/docs/

 → https://doc.traefik.io/