Journey to K3s: Accessing from the Outside
Up until now I have been working locally (on my home network). While that is enough for most of the services I’m running I need to access some of them from the outside. For example, I want to expose this blog to the internet and access Miniflux to read my RSS feeds on the go.
There are a few ways to achieve this but I have some specific requirements that I want to meet:
- Zero-trust approach: I don’t want to expose the services directly to the internet.
- Public services: Other clients apart from me should be able to access some of the services.
- Home IP safety: Don’t directly expose my home IP address. (This is on par with #1, but I want to make it explicit).
- On-transit encryption: Full on transit encryption from the client to the cluster with no re-encryption in the middle.
- No Cloudflare. (Breaks #4)
- No Tailscale. (Breaks #2, also there are other users at home and I don’t want to have the Tailscale client running all the time).
What does this leave me? A reverse proxy server.
I’m going to setup HAProxy in a separate external server to act as a reverse proxy that will connect to my home k3s cluster directly, but since the DNS records will point to this HAProxy server my home IP address will not be exposed. HAProxy won’t be able to decrypt the traffic but leverage the SSL SNI header to route the traffic back to the cluster for the allowed domains that I setup. This way I can have a zero-trust approach and have the traffic encrypted from the client to the cluster.
So, to start working I created a new VPS in Hetzner Cloud (affiliate link) and installed HAProxy in there, which is the easy part. Once the system is up to date and with the minimal services running I can start working on setting up HAProxy.
Passthrough traffic
Passthrough traffic is fairly simple, just create a frontend that listens on port 443 and sends the traffic to the ssl backend that check for SSL and sends data upstream to the k3s cluster. Since I’m are not decrypting the traffic TCP mode is used to tunnel the traffic.
I’m going to use ssl-hello-chk
option to ensure the traffic is SSL. This option checks if the first bytes of the connection are a valid SSL handshake, if not it will drop the connection.
|
|
Force SSL (redirect HTTP to HTTPS)
Since initially I’m not going to expose plain HTTP services I can just redirect all HTTP trafic to HTTPS: Just need to create a new frontend that listens on port 80 and redirects the traffic to the HTTPS frontend. This should work transparently for the client.
|
|
Deny non-allowed domains
For security reasons I want to deny access to all domains that are not in the allowed list: that is domains that I explicitly allow for outside access.
I’m going to create a file /etc/haproxy/allowed-domains.txt
with the list of domains separated by newlines and use the acl
directive to check if the domain is in the list abruptly droping the connection if it’s not.
The file /etc/haproxy/allowed-domains.txt
looks like this:
|
|
The new configuration options for the frontend part. No changes needed on the backend.
|
|
Conclusion
Once all this changes are in place I can restart the HAProxy service and the traffic should be routed to the k3s cluster so I can access the services from the outside without exposing my home IP address and have the traffic encrypted from the client to the cluster. Though not perfect this is a fairly simple and good setup, it requires manual labor but it’s a good tradeoff for the requirements I have.
Back when I set up Miniflux I created an ingress specifically for external access that wasn’t working since my cluster could not be reached by the ACME servers on the domain I set up. Now that I have HAProxy in place the domain can be setup to point to it and the traffic will be correctly routed to the cluster completing the configuration by requesting a certificate from Let’s Encrypt and exposing the Ingress to the internet.