# SSH (Gitea) with Traefik V2

**URL:** <https://community.traefik.io/t/ssh-gitea-with-traefik-v2/14525>\
**Category:** Traefik v2\
**Tags:** tcp, docker\
**Created:** [May 19, 2022, 1:33pm UTC](https://community.traefik.io/t/ssh-gitea-with-traefik-v2/14525 "2022-05-19T13:33:43Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![choque](https://avatars.discourse-cdn.com/v4/letter/c/5f8ce5/32.png) [@choque](https://community.traefik.io/u/choque)\
**Post date:** [May 19, 2022, 1:33pm UTC](https://community.traefik.io/t/ssh-gitea-with-traefik-v2/14525/1 "2022-05-19T13:33:43Z")

</div>

So for the last couple days I've been trying to get traefik and gitea to play nicely concerning the routing of gitea's ssh endpoint. It works if I don't route ssh via traefik at all, but as soon as I try to route it via traefik, I'll always get an error:

```auto
Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

```

Here are my configs:  
**Traefik docker-compose.yml**

```auto
version: "3"

services:
  traefik:
    image: traefik:latest
    container_name: traefik
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - proxy
    ports:
      - 80:80
      - 443:443
      - 53:53/udp
      - 53:53/tcp
      - 8080:8080
      - 2222:2222/tcp
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik.yml:/traefik.yml:ro
      - ./acme.json:/acme.json
      - ./config:/configurations
      - traefik-logs:/var/log/traefik
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=${TZ}
      - CF_API_EMAIL=${CF_API_EMAIL}
      - CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN}
    labels:
      - traefik.enable=true
      - traefik.docker.network=proxy
      - traefik.http.routers.traefik-secure.entrypoints=websecure
      - traefik.http.routers.traefik-secure.rule=Host(`${FQDN}`)
      - traefik.http.routers.traefik-secure.service=api@internal
      - traefik.http.routers.traefik-secure.middlewares=user-auth@file,safe-ipwhitelist@file
      - traefik.http.routers.whoami-secure.service=noop@internal
      - traefik.http.routers.whoami-secure.tls.domains[0].main=${MAIN_DOMAIN}
      - traefik.http.routers.whoami-secure.tls.domains[0].sans=${SANS_DOMAIN}
      - traefik.http.middlewares.authelia.forwardauth.address=http://authelia:9091/api/verify?rd=https://${AUTHELIA_FQDN}/
      - traefik.http.middlewares.authelia.forwardauth.trustforwardheader=true
      - traefik.http.middlewares.authelia.forwardauth.authresponseheaders=Remote-User, Remote-Groups

networks:
  proxy:
    external: true
volumes:
  traefik-logs:

```

**Traefik traefik.yml (excerpt)**

```auto
entryPoints:
  web:
    address: :80
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https

  websecure:
    address: :443
    http:
      middlewares:
        - secureHeaders@file
        - crowdsec-bouncer@file
      tls:
        certResolver: cloudflare

  ssh:
    address: :2222

```

**Gitea docker-compose.yml**

```auto
version: "3"

services:
  gitea-db:
    image: postgres:alpine
    container_name: gitea-db
    networks:
      - gitea
    volumes:
      - ./data/postgres:/var/lib/postgresql/data
    restart: unless-stopped
    environment:
      - TZ=${TZ}
      - POSTGRES_USER=${DB_USER}
      - POSTGRES_PASSWORD=${DB_PW}
      - POSTGRES_DB=${DB_NAME}

  gitea:
    image: gitea/gitea:1.16.8
    container_name: gitea
    networks:
      - gitea
      - proxy
    environment:
      - TZ=${TZ}
      - DOMAIN=${FQDN}
      - ROOT_URL=https://${FQDN}
      - START_SSH_SERVER=true
      - SSH_DOMAIN=${FQDN}
      - SSH_PORT=22
      - SSH_LISTEN_PORT=2222
      - DB_TYPE=postgres
      - DB_HOST=${DB_HOST}
      - DB_NAME=${DB_NAME}
      - DB_USER=${DB_USER}
      - DB_PASSWD=${DB_PW}
    depends_on:
      - gitea-db
    volumes:
      - ./data/gitea:/data
    labels:
      - traefik.enable=true
      - traefik.docker.network=proxy
      - traefik.http.routers.gitea-secure.rule=Host(`${FQDN}`)
      - traefik.http.routers.gitea-secure.entrypoints=websecure
      - traefik.http.routers.gitea-secure.service=gitea
      - traefik.http.services.gitea.loadbalancer.server.port=3000
      - 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

networks:
  gitea:
  proxy:
    external: true

```

With this, I can access gitea via my browser and get clone links that look like this

```auto
git@gitea.mydomain.com:choque/Test.git

```

But when I try to clone it via

```auto
git clone git@gitea.mydomain.com:choque/Test.git

```

I get the error posted at the beginning. And yes, the ssh key is added to my profile and like I said, it works when I don't route gitea's ssh service through traefik.

Any help is appreciated!

---

<div class="post-metadata">

**Author:** ![cakiwi](https://sea2.discourse-cdn.com/flex020/user_avatar/community.traefik.io/cakiwi/32/3205_2.png) [@cakiwi](https://community.traefik.io/u/cakiwi)\
**Post date:** [May 19, 2022, 2:03pm UTC](https://community.traefik.io/t/ssh-gitea-with-traefik-v2/14525/2 "2022-05-19T14:03:37Z")

</div>

Pretty sure this is gitea configuration related:

Serving on :222 working configuration

```yaml
  gitea:
    image: gitea/gitea
    container_name: gitea
    deploy:
      replicas: 1
      labels:
        traefik.enable: "true"
        traefik.http.routers.gitea.rule: Host(`gitea.example.com`)
        traefik.http.services.gitea.loadBalancer.server.port: 3000
        traefik.tcp.routers.gitea-ssh.entrypoints: git
        traefik.tcp.routers.gitea-ssh.rule: HostSNI(`*`)
        traefik.tcp.services.gitea-ssh.loadbalancer.server.port: 22
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - GITEA __repository__ ENABLE_PUSH_CREATE_ORG=true
      - GITEA __server__ OFFLINE_MODE=true
      - GITEA __server__ ROOT_URL=https://gitea.example.com/
      - GITEA __server__ SSH_PORT=222
      - GITEA __service__ DISABLE_REGISTRATION=true
      - GITEA __service__ REQUIRE_SIGNIN_VIEW=true
      - GITEA __service__ DEFAULT_ALLOW_CREATE_ORGANIZATION=false
      - GITEA __service__ DEFAULT_ENABLE_TIMETRACKING=false
      - GITEA __oauth2_client__ ENABLE_AUTO_REGISTRATION=true
      - GITEA __oauth2_client__ OPENID_CONNECT_SCOPES=email profile
      - GITEA __oauth2_client__ USERNAME=email
      - GITEA __openid__ ENABLE_OPENID_SIGNIN=false

```

---

<div class="post-metadata">

**Author:** ![choque](https://avatars.discourse-cdn.com/v4/letter/c/5f8ce5/32.png) [@choque](https://community.traefik.io/u/choque)\
**Post date:** [May 19, 2022, 2:50pm UTC](https://community.traefik.io/t/ssh-gitea-with-traefik-v2/14525/3 "2022-05-19T14:50:12Z")

</div>

Thanks for the reply @cakiwi . It works that way but as a result the git urls have ssh:// and the port added to them. So they look like this

```auto
ssh://git@gitea.mydomain.com:2222/choque/Test.git

```

When you set GITEA\_\_server\_\_SSH\_PORT to 22 it results in much nicer URLs

```auto
git@gitea.mydomain.com:choque/Test.git

```

That's why I tried setting GITEA\_\_server\_\_SSH\_PORT to 22 and GITEA\_\_server\_\_SSH\_LISTEN\_PORT to 2222. But I can't get it to work that way. I even tried using the built in ssh server via setting GITEA\_\_server\_\_START\_SSH\_SERVER to true

---

<div class="post-metadata">

**Author:** ![choque](https://avatars.discourse-cdn.com/v4/letter/c/5f8ce5/32.png) [@choque](https://community.traefik.io/u/choque)\
**Post date:** [May 23, 2022, 2:46pm UTC](https://community.traefik.io/t/ssh-gitea-with-traefik-v2/14525/4 "2022-05-23T14:46:19Z")

</div>

Anyone got an idea or a working configuration that does what I described?

---

<div class="post-metadata">

**Author:** ![choque](https://avatars.discourse-cdn.com/v4/letter/c/5f8ce5/32.png) [@choque](https://community.traefik.io/u/choque)\
**Post date:** [June 25, 2022, 10:00pm UTC](https://community.traefik.io/t/ssh-gitea-with-traefik-v2/14525/5 "2022-06-25T22:00:22Z")

</div>

Pushing for visibility.

---

<div class="post-metadata">

**Author:** ![choque](https://avatars.discourse-cdn.com/v4/letter/c/5f8ce5/32.png) [@choque](https://community.traefik.io/u/choque)\
**Post date:** [August 1, 2022, 6:57am UTC](https://community.traefik.io/t/ssh-gitea-with-traefik-v2/14525/6 "2022-08-01T06:57:20Z")

</div>

Still looking for a working configuration

---

<div class="post-metadata">

**Author:** ![bryanpedini](https://sea2.discourse-cdn.com/flex020/user_avatar/community.traefik.io/bryanpedini/32/5662_2.png) [@bryanpedini](https://community.traefik.io/u/bryanpedini)\
**Post date:** [August 1, 2022, 7:41am UTC](https://community.traefik.io/t/ssh-gitea-with-traefik-v2/14525/7 "2022-08-01T07:41:13Z")

</div>

I'm sorry but why you want Træfik to handle the SSH connection for the container?

Either you use Træfik to listen on port 22 and you forward that to the container (and you don't have the port specified in the URLs and such) or you use another port and you need to have that specified.  
Otherwise, if the host machine is Linux, use SSH redirection within the host configuration: [Installation with Docker - Docs](https://docs.gitea.io/en-us/install-with-docker/#ssh-container-passthrough)

---

<div class="post-metadata">

**Author:** ![1FD8473B08](https://avatars.discourse-cdn.com/v4/letter/1/f05b48/32.png) [@1FD8473B08](https://community.traefik.io/u/1FD8473B08)\
**Post date:** [August 24, 2022, 9:33pm UTC](https://community.traefik.io/t/ssh-gitea-with-traefik-v2/14525/8 "2022-08-24T21:33:53Z")

</div>

I solved it by using a macvlan/ipvlan network (with file provider):

/etc/traefik/dyn-config/gitea.yaml

```auto
tcp:
    routers:
        gitea-ssh-router:
            entryPoints:
                - ssh
            rule: HostSNI(`*`)
            service: gitea-ssh-service
    services:
        gitea-ssh-service:
            loadBalancer:
                servers:
                    - address: gitea:22

http:
    routers:
        router-gitea:
            rule: Host(`gitea.example.org`)
            service: service-gitea
            entryPoints:
                - web-secure

    services:
        service-gitea:
            loadBalancer:
                servers:
                    - url: http://gitea:3000

```

/etc/traefik/traefik.yaml

```auto
entryPoints:
    ssh:
        address: ":22"
    web:
        address: ":80"
        http:
            redirections:
                entryPoint:
                    to: web-secure
                    scheme: https
    web-secure:
        address: ":443"
        http:
            tls:
                certResolver: le

providers:
    file:
        directory: /etc/traefik/dyn-config
api:
    dashboard: true
global:
    sendAnonymousUsage: false
certificatesResolvers:
    le:
        acme:
            tlsChallenge: {}
            storage: /tls/acme.json

```

docker-compose.yaml for traefik:

```auto
services:
    traefik:
        image: traefik
        restart: always
        networks:
            traefik-net:
            macvlan50:
                ipv4_address: ${TRAEFIK_IP}
        expose:
            - "22"
            - "80"
            - "443"
        volumes:
            - ./dyn-config:/etc/traefik/dyn-config:ro
            - ./traefik.yaml:/etc/traefik/traefik.yaml:ro
            - ./tls:/tls

networks:
    traefik-net:
        name: traefik-net
    macvlan50:
        external: true

```

docker-compose.yaml for gitea

```auto
services:
    gitea:
        image: gitea/gitea
        environment:
            - USER_UID=1000
            - USER_GID=1000
            # server
            - GITEA __server__ OFFLINE_MODE=true
            - GITEA __server__ ROOT_URL=https://gitea.example.org/
            # database
            - GITEA __database__ DB_TYPE=postgres
            - GITEA __database__ HOST=db:5432
            - GITEA __database__ NAME=gitea
            - GITEA __database__ USER=gitea
            - GITEA __database__ PASSWD=gitea
        restart: always
        networks:
            - gitea
            - traefik-net
        volumes:
            - ./gitea:/data
            - /etc/timezone:/etc/timezone:ro
            - /etc/localtime:/etc/localtime:ro
        expose:
            - "3000"
            - "22"
        depends_on:
            - db

    db:
        image: postgres
        restart: always
        environment:
            - POSTGRES_USER=gitea
            - POSTGRES_PASSWORD=gitea
            - POSTGRES_DB=gitea
        networks:
            - gitea
        volumes:
            - ./postgres:/var/lib/postgresql/data

networks:
    gitea:
    traefik-net:
        external: true

```

Optional (but fancy):

```auto
http:
    routers:
        dashboard:
            entryPoints:
                - web-secure
            rule: Host(`traefik.example.org`) # && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))
            service: dashboard@internal
        api:
            entryPoints:
                - web-secure
            rule: Host(`traefik.example.org`) && PathPrefix(`/api`)
            service: api@internal

```

Best regards
