Another app I run is Gitea, to manage git repo's. We need SSH working, so that SSH keys can be used to push and pull, but this is a bit tricky in a Kubernetes environment.
When Traefik accepts requests on ports 80 and 443, it knows that the HTTP protocol is being used, so it checks for a Host: header, and uses that to figure out which Service should handle the request.
There is no such host indicator for SSH traffic, so we need to dedicate a port on the server itself (i.e. exposed to the outside world), and configure Traefik to simply route all traffic on that port to the Gitea container.
Exposing an external server port
To configure Traefik to listen on a new port, we need to change its "static configuration".
When we installed K3s, it auto-installed Traefik using Helm, using HelmChartConfig resources. If we want to change the config, all we have to do is drop a file in /var/lib/rancher/k3s/server/manifests/, where Helm will notice it, apply the changes and restart Traefik[1]It takes time for all this to happen, so watch the pods to see when it actually happens..
Create a file[2]Any name that starts with traefik- will work. called /var/lib/rancher/k3s/server/manifests/traefik-config.yaml that looks like this:
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: traefik
namespace: kube-system
spec:
valuesContent: |-
ports:
gitea-ssh:
port: 3022
expose: true
exposedPort: 3022
This creates a new "entry point" called gitea-ssh that Trafeik will listen on, along-side its default web (port 80) and websecure (port 443) entry points:
[core@vm-k3s ~]$ kubectl describe service traefik -n kube-system Name: traefik Namespace: kube-system Labels: app.kubernetes.io/instance=traefik-kube-system app.kubernetes.io/managed-by=Helm app.kubernetes.io/name=traefik helm.sh/chart=traefik-25.0.2_up25.0.0 Annotations: meta.helm.sh/release-name: traefik meta.helm.sh/release-namespace: kube-system Selector: app.kubernetes.io/instance=traefik-kube-system,app.kubernetes.io/name=traefik Type: LoadBalancer IP Family Policy: PreferDualStack IP Families: IPv4 IP: 10.43.201.207 IPs: 10.43.201.207 LoadBalancer Ingress: 10.2.2.33 Port: gitea-ssh 3022/TCP TargetPort: gitea-ssh/TCP NodePort: gitea-ssh 32616/TCP Endpoints: 10.42.0.14:3022 Port: web 80/TCP TargetPort: web/TCP NodePort: web 31539/TCP Endpoints: 10.42.0.14:8000 Port: websecure 443/TCP TargetPort: websecure/TCP NodePort: websecure 32003/TCP Endpoints: 10.42.0.14:8443 Session Affinity: None External Traffic Policy: Cluster Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal EnsuringLoadBalancer 72s (x2 over 44m) service-controller Ensuring load balancer Normal AppliedDaemonSet 72s (x2 over 44m) Applied LoadBalancer DaemonSet kube-system/svclb-traefik-52d3e851 Warning UnAvailableLoadBalancer 72s There are no available nodes for LoadBalancer Normal UpdatedLoadBalancer 71s (x2 over 44m) Updated LoadBalancer with new IPs: [] -> [10.2.2.33]
Configuring Gitea
The relevant part of my Deployment for Gitea looks like this:
containers:
- name: gitea
image: gitea/gitea:1.21.8
imagePullPolicy: Never
ports:
- name: web
containerPort: 3000
- name: ssh
containerPort: 22
which tells Kubernetes that the container is listening on 2 ports:
- 3000 (web): this is the main web interface
- 22 (ssh): this is for SSH activity
The Service looks like this:
ports:
- name: web
protocol: TCP
port: 80
targetPort: web
- name: ssh
protocol: TCP
port: 22
targetPort: ssh
which simply forwards traffic on ports 80 and 22 to the corresponding ports in the Deployment.
The important bits are the IngressRoute's. This one is for the web interface, and is the same as our demo app:
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: gitea-ingress-route
spec:
entryPoints:
- web
routes:
- match: Host(`gitea.k3s`)
kind: Rule
services:
- name: gitea-service
port: web
However, for this app, we have a second ingress:
apiVersion: traefik.io/v1alpha1
kind: IngressRouteTCP
metadata:
name: gitea-ingress-route-ssh
spec:
entryPoints:
- gitea-ssh
routes:
- match: HostSNI(`*`)
services:
- name: gitea-service
port: ssh
It has a different type[3]Since IngressRoute assumes the use of the HTTP protocol., and uses the new gitea-ssh entry point we created above. Routing rules need to have at least one condition, so we include a dummy "match everything" rule, and all traffic received on this entry point will be forwarded to gitea-service's ssh port.










I am a 
