I have gitea setup behind traefik and it's working nicely, HTTPS clones are working, but I cannot seem to setup SSH clones. I have read through quite a number of guides and topics and attempted to apply what they say which is how I've gotten to this point, which I feel is mostly right, but something isn't quite working. Would someone be able to review my config please?
SSH is running on port 22 in the Gitea container, I'm attempting to expose this as port 222 through traefik.
When I try to clone I get this error:
GIT_SSH_COMMAND="ssh -v" git clone ssh://git@gitea.domain.xyz:222/user/TestRepo.git
Cloning into 'TestRepo'...
OpenSSH_8.9p1 Ubuntu-3ubuntu0.1, OpenSSL 3.0.2 15 Mar 2022
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 19: include /etc/ssh/ssh_config.d/*.conf matched no files
debug1: /etc/ssh/ssh_config line 21: Applying options for *
debug1: Connecting to gitea.domain.xyz [ip] port 222.
debug1: connect to address [ip] port 222: Connection timed out
ssh: connect to host [domain] port 222: Connection timed out
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
I have ensured that UFW is allowing port 222 on my server
So in the static config traefik.yml I have an entrypoint setup for port 222:
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
http:
tls: {}
gitea_ssh:
address: ":222"
Then in the traefik docker I have port 222 forwarded to 222:
version: "3.4"
services:
traefik:
image: "traefik:latest"
ports:
- "80:80"
- "443:443"
- "222:222"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./acme.json:/acme.json"
- "./traefik.yml:/traefik.yml"
- "./dynamic-conf:/etc/traefik/dynamic/"
networks:
- web
networks:
web:
external: true
Then I have my gitea docker setup like so:
version: "3.8"
networks:
gitea:
external: false
web:
external: true
services:
server:
image: gitea/gitea:latest
container_name: gitea
environment:
- USER_UID=1000
- USER_GID=1000
- DB_TYPE=mysql
- DB_HOST=db:3306
- DB_NAME=gitea
- DB_USER=gitea
- DB_PASSWD=password
- RUN_MODE=prod
- DOMAIN=gitea.domain.xyz
- HTTP_PORT=3000
- ROOT_URL=https://gitea.domain.xyz
# SSH port displayed in clone URL.
- SSH_DOMAIN=gitea.domain.xyz
- SSH_PORT=222
# Port for the built-in SSH server
- SSH_LISTEN_PORT=22
restart: always
networks:
- gitea
- web
volumes:
- /srv/gitea:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
depends_on:
- db
labels:
- "traefik.enable=true"
- "traefik.http.routers.gitea.rule=Host(`gitea.domain.xyz`)"
- "traefik.http.routers.gitea.entrypoints=web"
- "traefik.http.routers.gitea.entrypoints=websecure"
- 'traefik.http.services.gitea.loadbalancer.server.port=3000'
- "traefik.backend=gitea"
- "traefik.docker.network=web"
- "traefik.default.protocol=http"
- "traefik.port=3000"
- "traefik.http.routers.gitea.tls=true"
- "traefik.http.routers.gitea.tls.certresolver=letsEncrypt"
- "traefik.http.routers.gitea.tls.domains[0].main=gitea.domain.xyz"
# SSH routing, can't route based on host so anything to port 222 will come to this container
- "traefik.tcp.routers.gitea-ssh.rule=HostSNI(`*`)"
- "traefik.tcp.routers.gitea-ssh.entrypoints=gitea_ssh"
- "traefik.tcp.routers.gitea-ssh.service=gitea-ssh-svc"
- "traefik.tcp.services.gitea-ssh-svc.loadbalancer.server.port=22"
db:
image: mariadb:latest
container_name: gitea_db
restart: always
environment:
- MYSQL_ROOT_PASSWORD=some_root_password
- MYSQL_USER=gitea
- MYSQL_PASSWORD=password
- MYSQL_DATABASE=gitea
networks:
- gitea
volumes:
- /srv/gitea/db:/var/lib/mysql
ports:
- 9090:8080
If it’s only TCP, not HTTP, use the right router:
traefik.tcp.routers.gitea
Sorry I don't quite follow what you mean I've done wrong, in my docker compose file I am using the tcp router for the ssh specific setup, the http router is used for the web front end.
You are right, misread your config.
Do you need this line?
No worries.
I commented it out and the web front end still works but no change to the error I get with SSH sadly.
Have you checked if gitea really has an open port 22 within the container?
Have you tried to replace gitea image with something like a debian:stable-slim
container to see if port 22 works?
I believe the ssh server in the gitea container is working just fine, I did try this before posting here, I probably should have mentioned it in my first post!
bash-5.1# ssh localhost
The authenticity of host 'localhost (127.0.0.1)' can't be established.
ED25519 key fingerprint is *
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'localhost' (ED25519) to the list of known hosts.
root@localhost: Permission denied (publickey).
if it wasn't running then it would have straight out refused the connection right?
I may try another container like you suggest, but I think I have something wrong in the traefik config so I'm sure it'll break in the same way.
So, your question got my thinking about have I checked ssh working within the container.
This led me to try connecting locally on the host to the container through port 222, so:
ssh -v -p 222 localhost
This then works! With this outpit:
ssh -p 222 localhost
The authenticity of host '[localhost]:222 ([127.0.0.1]:222)' can't be established.
ED25519 key fingerprint is *
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Failed to add the host to the list of known hosts (/home/pi/.ssh/known_hosts).
pi@localhost: Permission denied (publickey).
With the gitea container outputting this:
gitea | Invalid user pi from 172.18.0.2 port 34692
gitea | Connection closed by invalid user pi 172.18.0.2 port 34692 [preauth]
So this means it is working, traefik is working and is setup correctly. It's the connection from outside my pi to port 222 that isn't working for some reason, I've made sure that port 222 is allowed through UFW:
222/tcp ALLOW Anywhere
222/tcp (v6) ALLOW Anywhere (v6)
There doesn't seem anything in /var/log/ufw.log to show that a connection has been blocked from outside.
Is a simple ssh
working with Traefik and just git
is failing?
it's not git specifically but ssh from another system to my pi (running traefik and gitea).
So this doesn't work either:
ssh -v -p 222 gitea.domain.xyz
I fear this may be either a networking issue now or a gitea issue, I think my traefik setup is working just fine at forwarding the SSH traffic, as shown by doing ssh -v -p 222 localhost on the pi and it working (well denying access, but that shows connectivity).
yea this is some sort of networking thing that I don't understand, and I now realise this as I ssh to my pi by doing ssh pi@hostname.local rather than ssh pi@domain.xyz so it's going locally.
I found this:
nmap -p 22,222 domain.xyz
PORT STATE SERVICE
22/tcp filtered ssh
222/tcp filtered rsh-spx
nmap -p 22,222 hostname.local
PORT STATE SERVICE
22/tcp open ssh
222/tcp open rsh-spx
So doing:
git clone ssh://git@hostname.local:222/user/TestRepo.git
Works just fine! I don't really understand why hostname.local works though. I guess it's blocking non-local traffic, I may ask on the gitea forum, that is probably a better place for this now that I've found that my traefik config works just fine.
Example of a working SSH server behind Traefik proxy server.
Not very elegant, the SSH-server updates and installs on every restart.
docker-compose.yml
with Traefik, web-server and ssh-server:
version: '3.9'
services:
traefik:
image: traefik:v2.9.6
ports:
- target: 80
published: 80
protocol: tcp
mode: host
- target: 443
published: 443
protocol: tcp
mode: host
- target: 9000
published: 9000
protocol: tcp
mode: host
networks:
- proxy
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- traefik-certificates:/certificates
command:
- --providers.docker=true
- --providers.docker.network=proxy
- --providers.docker.exposedByDefault=false
- --entryPoints.web.address=:80
- --entryPoints.web.http.redirections.entryPoint.to=websecure
- --entryPoints.web.http.redirections.entryPoint.scheme=https
- --entryPoints.websecure.address=:443
- --entryPoints.websecure.http.tls=true
- --entryPoints.websecure.http.tls.certResolver=myresolver
- --entryPoints.tcp9000.address=:9000
- --api.debug=true
- --api.dashboard=true
- --log.level=DEBUG
- --accesslog=true
- --certificatesResolvers.myresolver.acme.email=mail@example.com
- --certificatesResolvers.myresolver.acme.storage=/certificates/acme.json
- --certificatesresolvers.myresolver.acme.tlschallenge=true
labels:
- traefik.enable=true
- traefik.http.routers.dashboard.entrypoints=websecure
- traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)
- traefik.http.routers.dashboard.tls.certresolver=myresolver
- traefik.http.routers.dashboard.service=api@internal
- traefik.http.routers.dashboard.middlewares=myauth
- 'traefik.http.middlewares.myauth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/'
whoami:
image: traefik/whoami:v1.8
networks:
- proxy
labels:
- traefik.enable=true
- traefik.http.routers.whoami.entrypoints=websecure
- traefik.http.routers.whoami.rule=Host(`whoami.example.com`)
- traefik.http.services.whoami.loadbalancer.server.port=80
debian:
image: debian:stable
networks:
- proxy
command: bash -c "apt update && apt install -y openssh-server sudo net-tools && service ssh start && while true; do sleep 1; done"
labels:
- traefik.enable=true
- traefik.tcp.routers.debian.entrypoints=tcp9000
- traefik.tcp.routers.debian.rule=HostSNI(`*`)
- traefik.tcp.services.debian.loadbalancer.server.port=22
networks:
proxy:
name: proxy
volumes:
traefik-certificates:
SSH command:
ssh -v -p 9000 root@<IP-or-FQDN>
Thanks for your very comprehensive example.
I realised I've been a dummy and I didn't forward port 222 through my router to my pi, which is why HTTPS checkouts on the domain name were working but SSH on the domain wasn't working (but was working while using hostname.local).
At least I learnt a bunch of stuff while bumbling through this. Thankyou for all your input here, your questioning really helped me along the way