certbot

certbot

  • Configurando traefik com ssh

    Esse parece ser um tópico um tanto quanto... distópico? Talvez nem tanto. Mas todas as referências sobre o assunto na Internet estão incompletos e geralmente são pra versões mais antigas do traefik. Traefik é um roteador de conexões que funciona tanto como programa chamado por systemd (ou outro sistema de init se for um BSD), ou por container com algo como docker-compose, ou ainda diretamente em kubernetes. E não só funciona como proxy-reverse: pode também atuar como um servidor http pra suas conexões. E não somente tcp como udp. E escrito em Go!

    Eu precisei colocar um servidor ssh atrás de um traefik. E deu um certo trabalho. Pra testar e mostrar aqui também, usei docker-compose pro serviço. Isso facilitou a configuração. Pra backend com ssh, subi uma instância do gitea com o ssh na porta 2222. Usei também certificados letsencrypt nas portas https do serviço.

    Como um plus, ainda adicionei um allowlist pra acessar o serviço somente de certos IPs.

    Vamos pra configuração então. Primeiramente do traefik. Pra subir, eu configurei pra ouvir nas portas 80 (http), 443 (https), 8080 (traefik dashboard) e 2222 (ssh). O conteúdo de traefik/docker-compose.yml:

      
    services:
      traefik:
        image: "traefik:v3.3"
        container_name: "traefik"
        restart: unless-stopped
        environment:
          - TZ=Europe/Stockholm
        ports:
          - "80:80"
          - "443:443"
          - "8080:8080"
          - "2222:2222"
        volumes:
          - "./letsencrypt:/letsencrypt"
          - "/var/run/docker.sock:/var/run/docker.sock:ro"
          - "./traefik.yml:/traefik.yml:ro"
        networks:
          - traefik
    
    networks:
      traefik:
        name: traefik    
     
    

    Junto com essa configuração de container, ainda inclui a configuração do serviço em traefik/traefik.yml:

      
    api:
      dashboard: true
      insecure: true
      debug: true
    entryPoints:
      web:
        address: ":80"
        http:
          redirections:
            entryPoint:
              to: websecure
              scheme: https
      websecure:
        address: ":443"
      ssh:
        address: ":2222"
    serversTransport:
      insecureSkipVerify: true
    providers:
      docker:
        endpoint: "unix:///var/run/docker.sock"
        exposedByDefault: false
        network: traefik
    certificatesResolvers:
      letsencrypt:
        acme:
          email: This email address is being protected from spambots. You need JavaScript enabled to view it.
          storage: /letsencrypt/acme.json
          httpChallenge:
            # used during the challenge
            entryPoint: web
    
    log:
      level: WARN
     
    

    Aqui é possível ver que defino novamente os entrypoints pra 80, 443 e 2222. E no 80 (http) eu redireciono pro 443 (https). E certificado do letsencrypt.

    Tudo pronto no lado do traefik. Agora é o ponto de subir o serviço e preparar algumas coisas pra testar.

      
    # docker compose -p traefik -f traefik/docker-compose.yml up -d
    [+] Running 1/1
     ✔ Container traefik  Started                                0.4s
     
    

    Dentro do diretório traefik será criado um diretório letsencrypt e dentro dele terá o arquivo acme.json com seus certificados.

    Primeiramente vamos subir um container de teste pra ver se tudo funciona. O próprio projeto traefik fornece um container chamado whoami pra isso. O conteúdo de whois/docker-compose.yml:

      
    services:
      whoami:
        container_name: simple-service
        image: traefik/whoami
        labels:
            - "traefik.enable=true"
            - "traefik.http.routers.whoami.rule=Host(`whoami.tests.loureiro.eng.br`)"
            - "traefik.http.routers.whoami.entrypoints=websecure"
            - "traefik.http.routers.whoami.tls=true"
            - "traefik.http.routers.whoami.tls.certresolver=letsencrypt"
            - "traefik.http.services.whoami.loadbalancer.server.port=80"
        networks:
            - traefik
    networks:
      traefik:
        name: traefik    
     
    

    E habilitando o serviço com docker-compose:

      
    # docker compose -p whoami -f whoami/docker-compose.yml up -d
    WARN[0000] a network with name traefik exists but was not created for project "whoami".
    Set `external: true` to use an existing network 
    [+] Running 1/1
     ✔ Container simple-service  Started                                              0.3s 
     
    

    E um simples teste do serviço:

      
    ❯ curl -I http://whoami.tests.loureiro.eng.br
    HTTP/1.1 308 Permanent Redirect
    Location: https://whoami.tests.loureiro.eng.br/
    Date: Thu, 24 Apr 2025 12:43:55 GMT
    Content-Length: 18
    
    ❯ curl  https://whoami.tests.loureiro.eng.br
    Hostname: 0da540e1039c
    IP: 127.0.0.1
    IP: ::1
    IP: 172.18.0.3
    RemoteAddr: 172.18.0.2:52686
    GET / HTTP/1.1
    Host: whoami.tests.loureiro.eng.br
    User-Agent: curl/8.5.0
    Accept: */*
    Accept-Encoding: gzip
    X-Forwarded-For: 1.2.3.4
    X-Forwarded-Host: whoami.tests.loureiro.eng.br
    X-Forwarded-Port: 443
    X-Forwarded-Proto: https
    X-Forwarded-Server: 4cacedb0129a
    X-Real-Ip: 1.2.3.4
     
    

    O primeiro curl pro endpoint na porta 80 (http) recebe um redirect pra https. Perfeito! O segundo, os dados do container de forma transparente.

    O serviço funciona! Mas pra http e https. Falta ssh na porta 2222, que está nessa porta pra não atrapalhar o uso do ssh normal do sistema.

    Pra isso eu configurei um gitea, como descrevi no início do artigo. Seu docker-compose.yml é o seguinte:

      
        services:
      server:
        image: gitea/gitea:1.23-rootless #latest-rootless
        container_name: gitea
        environment:
         - GITEA__database__DB_TYPE=postgres
         - GITEA__database__HOST=db:5432
         - GITEA__database__NAME=gitea
         - GITEA__database__USER=gitea
         - GITEA__database__PASSWD=A4COU5a6JF5ZvWoSufi0L1aomSkzqww7s1wv039qy6o=
         - LOCAL_ROOT_URL=https://gitea.tests.loureiro.eng.br
         - GITEA__openid__ENABLE_OPENID_SIGNIN=false
         - GITEA__openid__ENABLE_OPENID_SIGNUP=false
         - GITEA__service__DISABLE_REGISTRATION=true
         - GITEA__service__SHOW_REGISTRATION_BUTTON=false
         - GITEA__server__SSH_DOMAIN=gitea.tests.loureiro.eng.br
         - GITEA__server__START_SSH_SERVER=true
         - GITEA__server__DISABLE_SSH=false    
        restart: always
        volumes:
          - gitea-data:/var/lib/gitea
          - gitea-config:/etc/gitea
          - /etc/timezone:/etc/timezone:ro
          - /etc/localtime:/etc/localtime:ro
        #ports:
        #  - "127.0.0.1:3000:3000"
        #  - "127.0.0.1:2222:2222"
        depends_on:
          - db
        networks:
          - traefik
        labels:
          - "traefik.enable=true"
          - "traefik.http.routers.giteaweb.rule=Host(`gitea.tests.loureiro.eng.br`)"
          - "traefik.http.routers.giteaweb.entrypoints=websecure"
          - "traefik.http.routers.giteaweb.tls=true"
          - "traefik.http.routers.giteaweb.tls.certresolver=letsencrypt"
          - "traefik.http.services.giteaweb.loadbalancer.server.port=3000"
          - "traefik.http.middlewares.giteaweb-ipwhitelist.ipallowlist.sourcerange=1.2.3.4, 4.5.6.7"
          - "traefik.http.routers.giteaweb.middlewares=giteaweb-ipwhitelist"
          - "traefik.tcp.routers.gitea-ssh.rule=HostSNI(`*`)"
          - "traefik.tcp.routers.gitea-ssh.entrypoints=ssh"
          - "traefik.tcp.routers.gitea-ssh.service=gitea-ssh-svc"
          - "traefik.tcp.services.gitea-ssh-svc.loadbalancer.server.port=2222"
          - "traefik.tcp.middlewares.giteassh-ipwhitelist.ipallowlist.sourcerange=1.2.3.4, 4.5.6.7"
          - "traefik.tcp.routers.gitea-ssh.middlewares=giteassh-ipwhitelist"
    
      db:
        image: postgres:17
        restart: always
        container_name: gitea_db
        environment:
          - POSTGRES_DB=gitea
          - POSTGRES_USER=gitea
          - POSTGRES_PASSWORD=A4COU5a6JF5ZvWoSufi0L1aomSkzqww7s1wv039qy6o=
    
        networks:
          - traefik
        volumes:
          - gitea-db:/var/lib/postgresql/data
    
    networks:
      traefik:
        name: traefik
    
    volumes:
      gitea-data:
      gitea-config:
      gitea-db:
     
    

    Pode ser visto que existe uma regra pra parte web, que roda na porta 3000 do container, e pra parte de ssh, que fica na porta 22222. Eu deixei comentado a parte que exporta as portas pra mostrar claramente que isso não é necessário. Aliás não funciona se especificar as portas.

    E finalmente rodando o serviço:

      
    # docker compose -p gitea -f gitea/docker-compose.yml up -d
    WARN[0000] a network with name traefik exists but was not created for project "gitea".
    Set `external: true` to use an existing network 
    [+] Running 2/2
     ✔ Container gitea_db  Started                   0.3s 
     ✔ Container gitea     Started                   1.0s     
     
    

    E vamos ao testes.

      
    ❯ curl -s  https://gitea.tests.loureiro.eng.br | head -n 10
    <!DOCTYPE html>
    <html lang="en-US" data-theme="gitea-auto">
    <head>
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <title>Gitea: Git with a cup of tea</title>
            <link rel="manifest" href="data:application/json;base64,eyJuYW1lIjoiR2l0ZWE6IEdpdCB3aXRoIGEgY3VwIG9mIHRlYSIsInNob3J0X25hbWUiOiJHaXRlYTogR2l0IHdpdGggYSBjdXAgb2YgdGVhIiwic3RhcnRfdXJsIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwLyIsImljb25zIjpbeyJzcmMiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAvYXNzZXRzL2ltZy9sb2dvLnBuZyIsInR5cGUiOiJpbWFnZS9wbmciLCJzaXplcyI6IjUxMng1MTIifSx7InNyYyI6Imh0dHA6Ly9sb2NhbGhvc3Q6MzAwMC9hc3NldHMvaW1nL2xvZ28uc3ZnIiwidHlwZSI6ImltYWdlL3N2Zyt4bWwiLCJzaXplcyI6IjUxMng1MTIifV19">
            <meta name="author" content="Gitea - Git with a cup of tea">
            <meta name="description" content="Gitea (Git with a cup of tea) is a painless self-hosted Git service written in Go">
            <meta name="keywords" content="go,git,self-hosted,gitea">
            <meta name="referrer" content="no-referrer">
    ❯ telnet gitea.tests.loureiro.eng.br 2222
    Trying 1.2.3.4...
    Connected to gitea.tests.loureiro.eng.br.
    Escape character is '^]'.
    SSH-2.0-Go
    
    ^]
    telnet> q
    Connection closed.    
     
    

    E pronto! Temos o serviço funcionando e roteado pelo traefik. E podendo fazer ACL de ip do serviço sem precisar do firewall pra isso.

    Boa diversão!

    Nota: eu coloquei um básico da configuração do gitea pra exemplo. E não funciona se copiar e colar. Pra ter um serviço rodando, terá de adicionar mais algumas partes em environment.
We use cookies

We use cookies on our website. Some of them are essential for the operation of the site, while others help us to improve this site and the user experience (tracking cookies). You can decide for yourself whether you want to allow cookies or not. Please note that if you reject them, you may not be able to use all the functionalities of the site.