Para aprender um pouco mais sobre o K8S, comecei a executar um cluster K3S de servidor/nó único como um laboratório doméstico. Mas acredito que cheguei a um impasse na minha compreensão do modelo de rede, talvez específico do K3S.
Até aí tudo bem, exceto que eu queria aplicar um certificado TLS a alguns serviços + entradas que configurei.
Em um deles configurei um certificado TLS como segredo e apliquei no Ingress associado ao serviço, porém sempre consigo o TRAEFIK DEFAULT CERT
, que você pode ver aqui .
Pelo que entendi, o K3S vem com Traefik e ServiceLB pré-empacotados para não depender de balanceadores de carga externos de serviços em nuvem (AWS etc). Meu primeiro palpite foi que o Traefik iria "descobrir" as rotas configuradas em meu Ingress e fazer proxy do tráfego TLS, usando assim o certificado que configurei. Claramente, este não é o caso, então suponho que preciso configurar um certificado TLS para a própria instância do Traefik.
Minhas perguntas são então
- Como posso configurar este certificado no K3S e preciso de um certificado curinga se pretendo ter vários projetos/domínios atingindo este cluster? (Eu preferiria gerenciar os certificados por projeto)
- Quais são as funções do ServiceLB e do Traefik no modelo de rede K3S? Se o Traefik estiver obtendo o tráfego 80 e 443, ele está apenas encaminhando o tráfego para o ServiceLB, que o encaminhará para o recurso Ingress?
Caso seja necessário, aqui está minha configuração de entrada/serviço
resource kubernetes_service_v1 snitch_service {
metadata {
name = "snitch"
namespace = module.namespace.name
}
spec {
selector = {
app = "snitch"
}
type = "LoadBalancer"
port {
name = "main"
port = 3010
target_port = 3000
node_port = 30003
}
}
}
resource kubernetes_secret_v1 tls_secret {
metadata {
name = "snitch-tls-cert"
namespace = "my-namespace"
}
type = "kubernetes.io/tls"
data = {
"tls.crt" = base64encode(my_certificate)
"tls.key" = base64encode(my_certificate_private_key)
}
}
resource kubernetes_ingress_v1 snitch_ingress {
metadata {
name = "snitch"
namespace = "my-namespace"
annotations = {
"ingress.kubernetes.io/ssl-redirect" = "false"
}
}
spec {
tls {
hosts = [local.subdomain]
secret_name = "snitch-tls-cert"
}
rule {
host = local.subdomain
http {
path {
path = "/"
path_type = "Prefix"
backend {
service {
name = "snitch"
port {
number = 3010
}
}
}
}
}
}
}
}
Obrigado!
O serviço de entrada geralmente é onde ocorrem as terminações de TLS - ou seja, quando você tem um cliente como um navegador da Web acessando uma
https://
URL que aponta para seu cluster Kubernetes, o cliente está se conectando ao serviço de entrada, que negocia a conexão segura e, em seguida, faz proxy a conexão com seu serviço de back-end (muitos servidores de entrada têm suporte para tls de "passagem", onde a terminação realmente acontece no back-end, mas geralmente é mais fácil e com melhor desempenho ter a terminação tratada pelo serviço de entrada).Da documentação do Kubernetes :
k3s usa traefik como serviço de entrada, então você precisa configurar seus certificados SSL no traefik.
Configurando o certificado padrão
Na ausência de um certificado tls específico de entrada, o traefik usará um certificado padrão para proteger o tráfego tls. Pronto para uso, o traefik usará um certificado autoassinado. Supondo que meu cluster k3s esteja disponível no hostname
k3s.virt
, podemos vê-lo assim:Podemos seguir as instruções deste artigo para substituir o certificado padrão. Se a maioria dos nossos serviços for hospedada no mesmo domínio, faz sentido usar um certificado curinga. Se eu criar um novo certificado
*.example.com
e configurarTLSStore
conforme descrito no artigo vinculado, podemos ver que o Traefik agora está usando o certificado atualizado:Se eu implantar um pod, serviço e entrada, assim:
Então (assumindo que isso
whoami.example.com
resolve para o cluster) veremos que a entrada está usando o certificado padrão:Configurando certificados por Ingress
Se não quisermos depender do certificado padrão (por exemplo, se precisarmos usar um nome de host que não faça parte do domínio coberto pelo curinga), podemos configurar um certificado exclusivo para nosso serviço. Isso está descrito na documentação de entrada do traefik .
Primeiro, precisamos criar um certificado e colocá-lo em um segredo do Kubernetes. Então precisamos atualizar nosso Ingress para fazer referência ao certificado adicionando uma
spec.tls
seção:Com esse recurso implementado, vemos que o ingresso não está mais usando o certificado padrão:
Espero que isso esclareça um pouco as coisas. Os links de documentação que forneci aqui cobrem as coisas com muito mais detalhes.
Coloquei todos os manifestos que usei para testar esta configuração neste repositório .
Respondendo aos seus comentários:
ServiceLB é o que monitora os serviços
type: LoadBalancer
e expõe essas portas diretamente nos nós do cluster (consulte, por exemplo, estes documentos ). Por exemplo, Traefik como um serviço nokube-system
namespace semelhante a este:Portanto, o ServiceLB mapeia as portas 80 e 443 nos nós do cluster para este serviço.
Consegui fazer o TLS funcionar em parte graças à resposta de @larsks, isso me levou a entender que a configuração do K8S está brincando com a instância do Traefik e, portanto, consegui encontrar os logs e ver o problema.
Estou escrevendo uma resposta caso ela seja útil para outra pessoa no futuro.
Essencialmente, quando você adiciona/modifica um
Ingress
recurso, o K8S informa ao controlador do Ingress para se "configurar". No nosso caso, a instância Traefik lança uma configuração em si mesma dependendo do definidoIngress
(ou seja: adicionando rotas), e se houver TLS definido ela tenta adicionar o certificado no segredo ao seu TLSStore.Como a instância do Traefik está aplicando uma nova configuração, muitas informações necessárias podem ser encontradas nos logs. No meu caso, houve diversos problemas que impediram que o certificado fosse adicionado ao TLSStore . O que significa que o Traefik só tinha o "DEFAULT TLS CERT" disponível para encerrar. Eu esperava que ele usasse tudo o que eu colocasse no segredo, mesmo que fossem strings aleatórias.
Para K3S, você pode acompanhar os logs com:
A criação de um certificado autoassinado me permitiu validar a teoria, pois agora meu tráfego HTTPS estava sendo encerrado por esse certificado autoassinado e não pelo certificado padrão. A resposta do @larsks validou que minha configuração fazia sentido. Caso seja útil para alguém, você pode criar um certificado autoassinado e uma nova chave com este comando:
Como toque final, vários tutoriais falaram sobre inserir os "certificados codificados em base64" no segredo, o que significa apenas inseri-los no formato PEM, e não na codificação base64 do próprio PEM (que contém um bloco b64 dentro). Os logs do Traefik também me permitiram detectar esse problema. Você pode ver na pergunta original que eu estava usando
base64encode
o segredo. E se acontecer de você usar o Let's Encrypt, você pode concatenar o issuer_pem e o Certificate_pem para criar a cadeia (no meu caso,certificate_pem + issuer_pem
essa ordem faz o trabalho).