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 stunnel3
) and 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:
zfs
/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, NGINXThere 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 (22
,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-world
hello-world
servicehello-world
service 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-world
service automatically gets made accessible (with/without authentication) by a URL like https://hello-world.staging.<project>.<org>.paas.cloud
sshd
-capable container being created which which shares namespaces and resources with the hello-world
service container(s)ssh
in 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, ajaxterm
, and 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:
ssh
and securing access to it
fail2ban
NetworkPolicy
sautomountServiceAccountToken: false
DaemonSet
)traefik
is written in Go)sslh
ssh
and/or what responds over HTTPSSo 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 redis
clusters, 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 envoy
today.
Imagine being able to have the following URLs:
primary.redis.production.<project>.<org>.paas.cloud
primary.pg.production.<project>.<org>.paas.cloud
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).
sshd
serviceSo 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:
sshd-https.ingressroutetcp.yaml
---
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
Traefik’s 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):
sshd-tls.certificate.yaml
---
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
That 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):
sshd.svc.yaml
---
apiVersion: v1
kind: Service
metadata:
name: sshd
namespace: experiments
labels:
app: sshd
spec:
selector:
app: sshd
ports:
- name: sslh
protocol: TCP
port: 8443
And that Service
is going to work with our cluster CNI and forward that traffic to our sshd
deployment:
sshd.deployment.yaml
---
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.
kcup
to serve a single page over TLSAs 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 www-html
ConfigMap
) in our browser, since we’re not actually doing the SSH bit yet:
sslh
serviceOK 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 [176.9.30.135] 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 176.9.30.135 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:
sslh
at the absolute edge, could it possibly direct traffic properly? Seems like sslh
has to be at the edge
sslh
…?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
, but 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 sslh
)!
stunnel
+ sslh
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 proxytunnel -e
.
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 ProxyCommand
:
$ ssh -i ~/.ssh/personal/id_rsa root@ssh-over-https.experiments.vadosware.io -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.
deployment.yaml
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 stunnel
and 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.