tl;dr - You can expose SSH over the same port HTTPS runs on (443), turns out you can run a combination of
stunnel (in my particular case
sslh as sidecar containers that work together to some container that runs SSH (i.e.
sshd). At the ingress layer Traefik makes this easy to pull off by providing the
IngressRouteTCP CRD along with TLS passthrough. The infrastructure-as-code is up on GitLab.
A lot of the exploration I’ve been doing lately has been around running cloud workloads for other people. I’ve been essentially stockpiling knowledge that enables me to create a cloud provider – one of things I think people haven’t realized is that infrastructure has gotten so easy (for 80%+ of the cases out there) that it’s brought the cost of doing things like that down to a manage-able level for small teams. In the rush to the cloud and to complexity-as-a-service, I’m pretty steadily heading in the opposite direction – learning more about the fascinating world of operations. With this idea of running a cloud provider in my head, for the last few years it feels like I’ve been slowly searching the DevOps landscape to find the tools with with the highest leverage to complexity ratio and I’ve found some local (for now) maxima:
btrfs, OpenEBS’s idea of container-attached-storage)
containerd(containers), KubeVirt (vms), kata containers (containers as vms), firecracker (a VMM with lightweight VMs),
linkerd, Caddy, NGINX
There are lots of ideas floating around my head but I think one of the things I was really interested in was wondering all the things I could run over HTTPS. Wouldn’t it be cool to be able to
ssh sshd-c449d49d7-x78p4.<environment>.<project>.<org>.provider.tld, but have your traffic go out over HTTPS instead of the usual ports (
2222, etc)? I’m also interested in the idea of easily doing completely in-browser cloud-driven terminals into instances with SSH in the browser, without the forwarding normally associated with web based SSH. I don’t think I’ve seen that done before.
Here’s the flow I’m envisioning:
hello-worldservice is a minimal container that contains nothing but a binary and maybe some shared libraries (let’s say it was built with a language that makes this kind of thing easy, like Rust)
hello-worldservice automatically gets made accessible (with/without authentication) by a URL like
sshd-capable container being created which which shares namespaces and resources with the
sshin the terminal) pointing at the same URL,
ssh.debug.hello-world.staging.<project>.<org>.paas.cloud:443(yeah that’s a mouthful)
Some people look at SSHing into containers in any environment (staging/production) as wrong but I’m not so dogmatic about it – if the fastest and simplest way to fix something is to SSH in and maybe correct a misconfiguration live, or make some other risky change, I want to caution against it, but leave it possible. Which reminds me, I should probably chat about security.
Of course, I’m not the first person to think of a flow like this, and a lot of sites already have cloud consoles and SSH-over-HTTPS is essentially a solved problem. The above would look like this, if I used purely existing technology/approaches (or at least how I think they work, I’ve never worked at the companies that do this at scale, and probably never will):
How REST-ful commands get turned into native SSH interaction is the purview of applications like Apache guacamole and libraries like github-butlerx/wetty,
anyterm. That’s not really in scope for this talk except to note that they all work the same way – they run a HTTP server that works with some JS-side client code to. Visualized, this looks like:
Before we go on, a very vew choice words about security – security is hard. Generally, nothing is truly secure, but that’s no reason to not try. Doing things like this effectively multiplies the attack vectors of the things involved:
sshand securing access to it
traefikis written in Go)
sshand/or what responds over HTTPS
So this is going to be a bit of a handwave, but don’t think about taking what I’m about to discuss into production without properly considering a defense-in-depth (yeah it’s a buzzword, but it’s the nicest way to say “expect your shit will be compromised”) security posture. Probably just prepare for the worst like any good gray beard sysadmin would.
What’s interesting about this particular yak shave, anyway? Well what I think is novel is using
443 (HTTPS) for the actual routing of SSH itself. Don’t do JS things in the browser and then pass them to SSH, run a fully functional terminal in the browser emulator in the browser, and forward that to SSH directly. While I’m not sure that functionality is out there just yet there are some promising rumbles:
Anyway, today I’ll just use
ssh directly from my terminal to test it. The setup as a whole has a few benefits that I’m trying to explore:
So it’s a subtle difference, we’re basically getting rid of only one conceptual part (and really it’s more like we’re replacing it with another less complicated part, as we’ll find out soon). This approach I think could be very easily expanded though, and at some point we could be doing lots of things over 443, accessing
postgres, etc. Envoy is already well on the path of recognizing lots of protocols, the idea of just doing a bit of smart proxying is attractive though we won’t be using
Imagine being able to have the following URLs:
And not have to do any port juggling and opening/closing or worry about overly active firewalls! Things get even better if we start going over QUIC whenever that is fully stabilized and ready for widespread production use. Anyway, let’s keep moving.
Luckily for me, smarter people have already built the necessary tooling to make this happen! There are a few solutions out there:
One of the most widely deployed reverse proxies, NGINX is a high performance, trusted solution in the space, and I was able to rustle up exactly one link (I guess I didn’t search very hard) about doing this:
The key idea is simply to lean on NGINX’s capabilities as a TCP stream proxy.
A somewhat unexpected entry in this space is HAProxy is another well known and trusted solution in the proxying space, though it mostly operates at Layer 7 (HTTP-level). I was able to easily find a few more resources out there for doing this:
sslh(we’re gonna use this)
Seemingly much less widely known,
sslh (F/OSS software on GitHub) is a minimal tool that actually accomplishes this (and has a very nice README). One thing that’s interesting is that
sslh can actually support
openvpn and other protocols as one user opined on the mailing list.
sslh passes my sniff test for high quality library:
Since I’m using Traefik as my proxy,
sslh is the easiest thing to slot into my infrastructure and also happens to be the most featureful, as far as I can tell.
So where does Traefik come in? Well I’m going to be using Traefik as my reverse proxy, and it’s going to forward my TCP streams – making it very easy for me to spin up the debug container that’s running
sslh. Traefik, unlike some other ingress controllers has all the features I need here:
Traefik continues to be the most featureful ingress controller out there in the ecosystem as far as I’m concerned. The ergonomics are also pretty good – the CRDs aren’t so bad once you get used to them and they are implementing Gateway API support as they go. I’m not an ambassador anymore, so I’m not affiliated with Traefik the corpoation in any way (as I’ve had to disclaim in the past), so feels nice to say that’s my opinion, as unbiased as it could be (I’m obviously biased for Traefik since it’s useful and F/OSS and delivering lots of value for me).
So let’s get this show on the road, here are the resources we’ll be using. We’ll be standing up a
sshd “service”. I’ll describe it from the “outside” in:
--- kind: IngressRouteTCP apiVersion: traefik.containo.us/v1alpha1 metadata: name: sshd-https namespace: experiments spec: entryPoints: - websecure tls: secretName: experiments-sshd-tls passthrough: true routes: - kind: Rule match: HostSNI(`ssh-over-https.experiments.vadosware.io`) services: - name: sshd port: 8443
IngressRouteTCP custom resource does what it sounds like – they allow TCP traffic ingress into your cluster. One thing I’m going to want over the wide internet is that traffic to travel over HTTPS for the TLS (Transport Level Security) – We’ll be relying on
cert-manager for that (how to set it up is out of scope, but you should probably be running this on your cluster, no matter how you provision certs):
--- apiVersion: cert-manager.io/v1alpha2 kind: Certificate metadata: name: sshd-tls namespace: experiments spec: secretName: experiments-sshd-tls commonName: ssh-over-https.experiments.vadosware.io dnsNames: - ssh-over-https.experiments.vadosware.io issuerRef: name: letsencrypt-prod kind: ClusterIssuer
IngressRouteTCP custom resource is going to let the
traefik daemons (deployed as a
DaemonSet, of course) listening on every node know to point tha traffic to a Kubernetes
Service object (not a
TraefikService, which also exists):
--- apiVersion: v1 kind: Service metadata: name: sshd namespace: experiments labels: app: sshd spec: selector: app: sshd ports: - name: sslh protocol: TCP port: 8443
Service is going to work with our cluster CNI and forward that traffic to our
--- apiVersion: apps/v1 kind: Deployment metadata: name: sshd namespace: experiments spec: replicas: 1 selector: matchLabels: app: sshd template: metadata: labels: app: sshd spec: containers: # whoami shows web visitors some basic information about the container - name: whoami image: traefik/whoami args: - -key=/etc/tls/tls.key - -cert=/etc/tls/tls.crt - -name=ssh-over-https.experiments.vadosware.io - -port=443 ports: - containerPort: 443 volumeMounts: - name: tls mountPath: /etc/tls # sshd gives ssh visitors web access if they have the right credentials - name: sshd # 1.3.0 as of 05/07/2021 image: panubo/sshd@sha256:b5435f6c5a667ae8c3bd7ea6571612e888515b42d5c78b3c2d0e05bd93bcb365 imagePullPolicy: IfNotPresent resources: requests: cpu: 500m memory: 1Gi limits: cpu: 1000m memory: 2Gi env: - name: SSH_ENABLE_ROOT value: "true" - name: SSH_ENABLE_PASSWORD_AUTH value: "false" - name: DISABLE_SFTP value: "true" volumeMounts: - name: keys mountPath: /etc/authorized_keys # SSLH proxies the right streams to the right places - name: sslh # alpine:3.13.5 as of 05/07/21 image: alpine:3.13.5 imagePullPolicy: IfNotPresent command: - /bin/ash - -xc # This is not a great way to install sslh but # for some quick prototyping container... this will do # (https://github.com/yrutschle/sslh/pull/205) # # NOTE: transparent proxying does not work out of the box - | echo "installing sslh (very wastefully)..."; apk add -X http://dl-cdn.alpinelinux.org/alpine/edge/testing sslh echo "running sslh..." sslh \ --foreground \ --verbose=2 \ --listen=$(SSLH_LISTEN_ADDR) \ --ssh=$(SSLH_SSH_ADDR) \ --tls=$(SSLH_TLS_ADDR) ports: - containerPort: 8443 env: - name: SSLH_LISTEN_ADDR value: 0.0.0.0:8443 - name: SSLH_SSH_ADDR value: localhost:22 # same-pod containers share localhost - name: SSLH_TLS_ADDR value: localhost:443 # same-pod containers share localhost volumes: - name: www-html configMap: name: www-html - name: keys configMap: name: sshd-authorized-keys - name: tls secret: secretName: experiments-sshd-tls
There are some bits left out but you should get the idea. Check out the full code listing on GitLab if you’d like.
kcupto serve a single page over TLS
As for the travelers visiting that are not speaking
ssh I want to serve a small page. In the example deployment above I’m using
traefik/whoami but what I really want to use is a tool I wrote called
kcup (which I’ve written about in the past) to serve a simple HTML5 page, but basically you could put anything there that is capable of handling TLS (since TLS will be passed through). I actually added the code to support TLS to
kcup just for this post, so here’s what that looks like:
# kcup shows web visitors a basic web page - name: kcup # kcup @ v0.2.1 as of 05/09/2021 image: registry.gitlab.com/mrman/kcup-rust/cli@sha256:1f34f74670ce0ea711d495669a3a0f262f09a01cebd1179b93b2cf5e68464537 resources: requests: cpu: 50m memory: 50Mi limits: cpu: 200m memory: 200Mi env: - name: HOST value: "127.0.0.1" - name: PORT value: "8080" # 8443 for TLS - name: FILE value: /www/index.html - name: TLS_KEY value: /etc/tls/tls.key - name: TLS_CERT value: /etc/tls/tls.crt volumeMounts: - name: tls mountPath: /etc/tls - name: www-html mountPath: /www
At this point all we can do is look at the nice page (whose content is read from the
ConfigMap) in our browser, since we’re not actually doing the SSH bit yet:
OK on to the SSH bit. We want our traffic to actually go to
sslh instead of straight to the
kcup container – and
sslh will decipher the protocols and do the right redirection. The
sslh container is already in the
Deployment, and that config might work so let’s try it out:
$ ssh -i ~/.ssh/personal/id_rsa ssh-over-https.experiments.vadosware.io -p 443 -v mrman 00:30:23 [ssh-over-https-with-traefik] $ ssh -i ~/.ssh/personal/id_rsa ssh-over-https.experiments.vadosware.io -p 443 -v OpenSSH_8.6p1, OpenSSL 1.1.1k 25 Mar 2021 debug1: Reading configuration data /home/mrman/.ssh/config debug1: /home/mrman/.ssh/config line 6: Applying options for *.experiments.vadosware.io debug1: Reading configuration data /etc/ssh/ssh_config debug1: Connecting to ssh-over-https.experiments.vadosware.io [188.8.131.52] port 443. debug1: Connection established. debug1: identity file /home/mrman/.ssh/personal/id_rsa type 0 debug1: identity file /home/mrman/.ssh/personal/id_rsa-cert type -1 debug1: Local version string SSH-2.0-OpenSSH_8.6 debug1: kex_exchange_identification: banner line 0: HTTP/1.1 400 Bad Request debug1: kex_exchange_identification: banner line 1: Content-Type: text/plain; charset=utf-8 debug1: kex_exchange_identification: banner line 2: Connection: close debug1: kex_exchange_identification: banner line 3: kex_exchange_identification: Connection closed by remote host Connection closed by 184.108.40.206 port 443
Job… not done. Looks like there’s something wrong here – I’m getting HTTP as a response where SSH is expecting to get SSH messages! It looks like Traefik is actually intercepting the SSH (it never makes it back to
sslh, a quick look at the logs shows no movement) and expecting HTTPS to come through.
So here’s the first big issue, and it’s a rather subtle one –
sslh is not getting consulted at all about the incoming SSL-over-443 connection, because Traefik is attempting to disassemble it and failing, and returning a 400 as a result (before hitting the backend itself). Since I need SNI (which is provided by TLS) to have a chance at hosting multiple SSH endpoints on the same machine, it looks like I have to find out a way to get past this.
Some Q/A I had with myself:
sslhat the absolute edge, could it possibly direct traffic properly? Seems like
sslhhas to be at the edge
The first step in that terrible wild idea I settled on was adding
ProxyCommand for my SSH connections:
Host ssh-over-https.experiments.vadosware.io ProxyCommand openssl s_client -quiet -servername %h -connect %h:443
But alas, the wild crazy idea doesn’t work – the connection actually gets through, and I can see it show up in the logs of
sslh still thinks the connection is TLS, so it forwards the connection to the wrong place. I need to unwrap a layer. I even tried doing something like having a second
sslh instance to try and tear it apart (thinking that SSLH was tearing apart one layer at a time). Luckily for me though, there’s another way, and also basically well known at this point (so much so that it’s in the docs of
Turns out the resolution to this issue is actually quite simple (because other people have worked hard on it already) – using
stunnel in combination with
sslh, and using that
ProxyCommand as my
After resolving some issues, changing configuration, I got it working. First the server-side logs:
$ k logs -f sshd-c449d49d7-x78p4 -c sshd [info] copying files /authorized-users to /etc/authorized_keys [info] changing permissions on /etc/authorized_keys, /root/.ssh [info] starting sshd... chmod: /root/.ssh/authorized_keys: Read-only file system > Starting SSHD >> Generating new host keys ssh-keygen: generating new host keys: RSA DSA ECDSA ED25519 >>> Fingerprints for dsa host key 1024 MD5:01:57:dd:be:89:ab:c3:c7:d9:d7:75:56:51:91:2d:fe root@sshd-c449d49d7-x78p4 (DSA) 1024 SHA256:da03+OK9FRph7N++bFcJyTXvbFCk+gHlgq4vX8A5/3I root@sshd-c449d49d7-x78p4 (DSA) 1024 SHA512:8gr8DwIx4afGbRV6qF6fABbrVwa8hEmoXesDPz+Sh04WuYyGZ/iPKYEltrue4pY5b8/KA/Hc6u9YZZ3HvHyLZw root@sshd-c449d49d7-x78p4 (DSA) >>> Fingerprints for rsa host key 3072 MD5:96:c3:ec:5a:dc:60:00:7b:5d:f6:7d:aa:de:65:94:9a root@sshd-c449d49d7-x78p4 (RSA) 3072 SHA256:Pa1pi64e4v1KoGeDFLS2MUgyUwsu43LOhuMYXi4RK4k root@sshd-c449d49d7-x78p4 (RSA) 3072 SHA512:sxuEcLxoWeB1IALJe/QMIXhn7JW5lmQ1vzPcf9TLJDNbr9h2kyFl0Zboo3l6RoPHIkgKee5bFAPMOuR2D0ELcA root@sshd-c449d49d7-x78p4 (RSA) >>> Fingerprints for ecdsa host key 256 MD5:0a:2b:83:8e:4a:92:68:d5:9f:6b:7b:62:c5:26:45:39 root@sshd-c449d49d7-x78p4 (ECDSA) 256 SHA256:P6q5KtLJpHKEp8ud4fbC+L+Pbjw6YHHdHO8X2QhEGh8 root@sshd-c449d49d7-x78p4 (ECDSA) 256 SHA512:nMqKAUd/R8dZ6jpwmZe37QeZ0LkUxRupIYpiJDxVINPJsdTlHn35xvvEjYDguivBFHo4p3crGy+UhyZWZra7IQ root@sshd-c449d49d7-x78p4 (ECDSA) >>> Fingerprints for ed25519 host key 256 MD5:ac:bd:90:f3:5b:e5:e2:68:39:13:70:e4:28:44:9d:7a root@sshd-c449d49d7-x78p4 (ED25519) 256 SHA256:002QZIOY529lGBGCGE6liTrWK8l9sen+3+t/vz/8Xmw root@sshd-c449d49d7-x78p4 (ED25519) 256 SHA512:eY9XAlL4nvzn7N1Ph6WFSKcA/vCNNG0FWcQnCuO71uXvcNxzq2UbA+dqY0EFIeKt0Uqw0rk/Q1gTMeKR7R5glQ root@sshd-c449d49d7-x78p4 (ED25519) >> Unlocking root account INFO: password authentication is disabled by default. Set SSH_ENABLE_PASSWORD_AUTH=true to enable. Running /usr/sbin/sshd -D -e -f /etc/ssh/sshd_config Server listening on 0.0.0.0 port 22. Server listening on :: port 22. Accepted publickey for root from ::1 port 41448 ssh2: RSA SHA256:g6bCJRwmZFusZ/jyjl3vKB43cSZNjLpD+arXulk4wEk
On the client side, using
ssh in the terminal with that custom
$ ssh -i ~/.ssh/personal/id_rsa firstname.lastname@example.org -p 443 depth=2 C = US, O = Internet Security Research Group, CN = ISRG Root X1 verify return:1 depth=1 C = US, O = Let's Encrypt, CN = R3 verify return:1 depth=0 CN = ssh-over-https.experiments.vadosware.io verify return:1 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! Someone could be eavesdropping on you right now (man-in-the-middle attack)! It is also possible that a host key has just been changed. The fingerprint for the ED25519 key sent by the remote host is SHA256:002QZIOY529lGBGCGE6liTrWK8l9sen+3+t/vz/8Xmw. Please contact your system administrator. Add correct host key in /home/mrman/.ssh/known_hosts to get rid of this message. Offending ED25519 key in /home/mrman/.ssh/known_hosts:47 Password authentication is disabled to avoid man-in-the-middle attacks. Keyboard-interactive authentication is disabled to avoid man-in-the-middle attacks. UpdateHostkeys is disabled because the host key is not trusted. Welcome to Alpine! The Alpine Wiki contains a large amount of how-to guides and general information about administrating Alpine systems. See <http://wiki.alpinelinux.org/>. You can setup the system with the command: setup-alpine You may change this message by editing /etc/motd. sshd-c449d49d7-x78p4:~#
Beautiful – this means that with the tooling I’ve got set up in my cluster (Traefik, Cert-Manager, etc), I’m basically prepared to offer a dynamic, shared-namespace terminals over HTTPS to anyone that wants to rock up and use one (with the small caveat that they need to proxy first). It’s not as simple as I wanted –
stunnel had to be included, and I ended up not needing the HTTP/TLS support I added to
kcup, but all-in-all thanks to these giants I’ve got something I think is pretty cool.
Here’s how it works now:
Definitely not the simplicity I was going for but it does work!
This is where I’d link you to the awesome cloud provider I built that uses this tech but I don’t have that yet, and probably won’t for years. If you’re interested in that (or other things I build/write about), get in touch or join the mailing list! Always trying to deliver more value to the mailing list members, lately releasing some posts early and probably in the future giving discounts on services I run.
Before You should check out the GitLab repo for the details, but here’s a sneak peak of what the added complexity
--- apiVersion: apps/v1 kind: Deployment metadata: name: sshd namespace: experiments spec: replicas: 1 selector: matchLabels: app: sshd template: metadata: labels: app: sshd spec: containers: # SSLH proxies the right streams to the right places - name: sslh # alpine:3.13.5 as of 05/07/21 image: alpine:3.13.5 imagePullPolicy: IfNotPresent command: - /bin/ash - -xc # This is not a great way to install sslh but # for some quick prototyping container... this will do # (https://github.com/yrutschle/sslh/pull/205) # # NOTE: transparent proxying does not work out of the box - | echo "installing stunnel & sslh (very wastefully)..."; apk add perl stunnel; apk add -X http://dl-cdn.alpinelinux.org/alpine/edge/testing sslh; echo "creating combined CA + private key PEM file @ /combined.pem" cat /etc/tls/tls.crt /etc/tls/tls.key > /etc/tls.combined.pem echo "running stunnel + sslh..."; /usr/bin/stunnel3 \ -f \ -p $(TLS_COMBINED_CERT) \ -d $(LISTEN_ADDR) \ -l /usr/sbin/sslh -- \ --inetd \ --verbose=2 \ --ssh=$(SSLH_SSH_ADDR) \ --http=$(SSLH_HTTP_ADDR) ports: - containerPort: 8443 env: - name: LISTEN_ADDR value: 0.0.0.0:443 - name: TLS_COMBINED_CERT value: /etc/tls.combined.pem - name: SSLH_SSH_ADDR value: localhost:22 # same-pod containers share localhost - name: SSLH_HTTP_ADDR value: localhost:8080 # same-pod containers share localhost volumeMounts: - name: tls mountPath: /etc/tls ############# # Workloads # ############# # sshd gives ssh visitors web access if they have the right credentials - name: sshd # 1.3.0 as of 05/07/2021 image: panubo/sshd@sha256:b5435f6c5a667ae8c3bd7ea6571612e888515b42d5c78b3c2d0e05bd93bcb365 imagePullPolicy: IfNotPresent command: - /bin/ash - -c - | echo "[info] copying files /authorized-users to /etc/authorized_keys"; cp -r /authorized-users/* /etc/authorized_keys; echo "[info] changing permissions on /etc/authorized_keys, /root/.ssh"; chmod -R 700 /etc/authorized_keys; chmod -R 700 /root/.ssh; echo "[info] starting sshd..."; /entry.sh /usr/sbin/sshd -D -e -f /etc/ssh/sshd_config; resources: requests: cpu: 500m memory: 1Gi limits: cpu: 1000m memory: 2Gi env: - name: SSH_ENABLE_ROOT value: "true" - name: SSH_ENABLE_PASSWORD_AUTH value: "false" - name: DISABLE_SFTP value: "true" volumeMounts: - name: ssh-root-authorized-keys mountPath: /root/.ssh/authorized_keys subPath: authorized_keys readOnly: true - name: ssh-authorized-users mountPath: /authorized-users readOnly: true # kcup shows web visitors a basic web page - name: kcup # kcup @ v0.2.0 as of 05/09/2021 image: registry.gitlab.com/mrman/kcup-rust/cli@sha256:1f34f74670ce0ea711d495669a3a0f262f09a01cebd1179b93b2cf5e68464537 resources: requests: cpu: 50m memory: 50Mi limits: cpu: 200m memory: 200Mi env: - name: HOST value: "127.0.0.1" - name: PORT value: "8080" # 8443 for TLS - name: FILE value: /www/index.html # - name: TLS_KEY # value: /etc/tls/tls.key # - name: TLS_CERT # value: /etc/tls/tls.crt volumeMounts: # - name: tls # mountPath: /etc/tls - name: www-html mountPath: /www volumes: - name: www-html configMap: name: www-html - name: ssh-authorized-users configMap: name: ssh-authorized-users defaultMode: 0600 - name: ssh-root-authorized-keys configMap: name: ssh-root-authorized-keys defaultMode: 0600 - name: tls secret: secretName: experiments-sshd-tls
A few pages that I kept open while I was looking at all this and pieceing it together:
One way that this setup could be massively simplified is actually building
sslh into Traefik itself – a plugin that runs
sslh right after a connection is made would reduce the toil of getting this to work to zero, pretty quickly.
Before taking this into production, a nice review of the additional attack surface along with how I can monitor the access and properly report on it would be a good idea. Also, a check into the degraded overhead introduced by
sslh might make sense, but in general the
ssh.* prefix probably shouldn’t be being hit that often!
It was fun to explore this idea and get a feel for enabling SSH over HTTPS. Again, the F/OSS world delivers tons of value for free – all you have to do is have some ideas on how to put it all together at this point.
sslh is a pretty featureful and stable-looking tool and I’d be surprised to have many problems with it.