Replicating Gitea Docker SSH Passthrough on K3s

If you are selfhosting Gitea on a single node Kubernetes cluster and want to enable git through SSH while keeping SSH connection to the cluster, this guide is for you.

Update

As of Gitea Helm Chart v9.0.0, Gitea is not a StatefulSet but a Deployment instead. In order to make this work you have to replace pod pod name gitea-0 with:

$(kubectl get po -n gitea -l app=gitea -o name --no-headers=true)

Command substitution does not work in AuthorizedKeysCommand so I suggest creating a separate script along the lines of /usr/local/bin/gitea-shell

Background

I am currently in the process of migrating my selfhosted applications from docker-compose to Kubernetes. One of my most used selfhosted app is Gitea. I use it to host my projects, dotfiles and config files where I don’t expect any contributions or I simply want to keep it more private. In my docker-compose setup I used SSH Container Passthrough from Gitea docs but when I moved Gitea to k3s I couldn’t find any guides on how to achieve the same thing.

I installed Gitea using the official Helm Chart. The documentation says this about enabling SSH:

If you’re using ingress and want to use SSH, keep in mind, that ingress is not able to forward SSH Ports. You will need a LoadBalancer like metallb and a setting in your SSH service annotations.

However using this method will route all incoming SSH connections to the Gitea container, essentialy disabling SSH connection to the host. Therefore we need a way to pass SSH connections to user git to our Gite container running on Kubernetes and at the same time allow SSH connections to host for some other user(s)

Kubernetes Setup

Create user git on your host and deploy Gitea to Kubernetes (e.g. using Helm). You don’t need to expose port 22 using a service.

First we are going to create a new login shell for user git. Create file /usr/local/bin/gitea-shell with content:

#!/bin/sh
/usr/local/bin/kubectl exec -i -n gitea gitea-0 -c gitea -- env SSH_ORIGINAL_COMMAND="$SSH_ORIGINAL_COMMAND" /bin/sh "$@"

Your namespace might be different.

Then run as root (or sudo):

chmod +x /usr/local/bin/gitea-shell
usermod -s /usr/local/bin/gitea-shell git

Now everytime the user git logs in (i.e. using git via SSH) /usr/local/bin/gitea-shell gets executed which means our original SSH command will be executed in the gitea container.

Finally we need to make sure that the SSH keys we add through Gitea interface allow us to ’login’ as git user.

Edit /etc/SSH/SSHd_config and add the following:

Match User git
    AuthorizedKeysCommandUser git
    AuthorizedKeysCommand /usr/local/bin/kubectl exec -i -n gitea gitea-0 -c gitea -- /usr/local/bin/gitea keys -e git -u %u -t %t -k %k

If you are using AllowUsers directive don’t forget to add user git

Testing

Open Gitea in web browser and add you SSH key. Then try to SSH into the Gitea container.

ssh git@<your gitea url>

You should get a message like this:

PTY allocation request failed on channel 0 Hi there, test! You’ve successfully authenticated with the key named , but Gitea does not provide shell access. If this is unexpected, please log in with password and setup Gitea under another user. Connection to closed.

Now you can create a repo and you should be able to clone and push via SSH.