summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrtk0c <[email protected]>2025-10-24 21:11:33 -0700
committerrtk0c <[email protected]>2025-10-24 21:11:33 -0700
commit46944e9b1f93dde6418c6684621a56dc17ccf0f1 (patch)
tree9888ec87135334f317b213013628ee10ba1a938e
parente324f4feaaafa9b71c20e8eda1e59ca618040679 (diff)
Finish hand-rolled-ngrok article for publishing
Add HTTP & redirects section, remove Wireguard section because I haven't done it, and probably won't because tailscale is just so much eaiser. Some editorial direction to not "shits and giggles".
-rw-r--r--content/blog/hand-rolled-ngrok-over-protonvpn.md150
1 files changed, 146 insertions, 4 deletions
diff --git a/content/blog/hand-rolled-ngrok-over-protonvpn.md b/content/blog/hand-rolled-ngrok-over-protonvpn.md
index 55168bd..fa1e88c 100644
--- a/content/blog/hand-rolled-ngrok-over-protonvpn.md
+++ b/content/blog/hand-rolled-ngrok-over-protonvpn.md
@@ -1,11 +1,12 @@
---
title: "I paid for the whole vpn, so I'm damn well going to use the whole vpn"
+summary: "Or: hand roll a ngrok with protonvpn port forwarding for shenanigans"
+date: 2025-10-24T21:00:00-07:00
tags: ["sysadmin"]
categories: ["Life of a sysadmin"]
-draft: true
---
-_Or: hand roll a ngrok with protonvpn port forwarding, for shits and giggles_
+_Or: hand roll a ngrok with protonvpn port forwarding for shenanigans_
# Dumbness: port forward `sshd`
@@ -92,6 +93,147 @@ Enable and immediately start both services:
Read the public address under `/run/protonvpn`, now you should be able to ssh, from anywhere on the internet, to this machine.
-# Sillinessw: port forward wireguard
+# Silliness? port forward HTTP
-TODO
+It's basically the same process as for sshd.
+Make your HTTP server of choice, nginx or whatever, listen on 10.2.0.2:80 (or some other port of your choice). Maybe even setup DNS and a certificate for it. And then you can just access it on the public internet like any other server. That's it.
+
+Now, please don't do any nefarious things with this...
+That's how we lost port forwarding from mullvad. The curious reader who has not yet caught up on this matter may perform an internet search on their own behalf.
+
+# Automatically redirect to current port allocation
+
+Remembering that magic port number is pretty painful.
+Keeping it in links you save or send to friends is also pretty painful.
+Wouldn't it be nice to have some kind of short url service, that just redirects you to the current IP:port assigned, as of accessing?.
+
+For example, you can go to [https://sh.example.com/foo/bar], and be redirected to [https://funny-business.example.com:12345/foo/bar]. And when
+
+The python server that generates the redirects, and exposes an API for updating the mappings for our natpmp script to work with.
+
+```python
+#!/usr/bin/python
+
+# example config file
+"""
+server-1 i.example.com
+server-2 ii.example.com
+this-is-a-joke foo.example.com:6969
+"""
+
+import sys
+from http.server import BaseHTTPRequestHandler, HTTPServer
+from urllib.parse import urlparse, parse_qs
+
+config = {}
+config_path = sys.argv[1]
+
+PARENT_DOMAIN = "example.com"
+
+with open(config_path) as f:
+ for line in f:
+ line = line.strip()
+ if not line or line.startswith('#'):
+ continue
+ prefix, port = line.split(maxsplit=1)
+ config[prefix] = port
+
+def update_config(prefix, port):
+ config[prefix] = port
+ with open(config_path, 'w') as f:
+ for prefix, port in config.items():
+ f.write(f"{prefix} {port}\n")
+
+class RedirectHandler(BaseHTTPRequestHandler):
+ def do_POST(self):
+ p = urlparse(self.path)
+ if p.path != "/-/config":
+ return
+ q = parse_qs(p.query)
+ update_config(q['prefix'][0], int(q['port'][0]))
+
+ self.send_response(200)
+ self.end_headers()
+
+ def do_GET(self):
+ # self.path[0] is always '/', start at idnex 1 to ignore that
+ split_pt = self.path.find('/', 1)
+ if split_pt == -1:
+ split_pt = len(self.path)
+ prefix = self.path[1:split_pt]
+ rest = self.path[split_pt:]
+
+ if port := config[prefix]:
+ self.send_response(302)
+ self.send_header('Location', f"https://{prefix}.{PARENT_DOMAIN}:{port}{rest}")
+ self.end_headers()
+ return
+
+ self.send_response(404)
+ self.end_headers()
+
+HTTPServer(('127.0.233.80', 8046), RedirectHandler).serve_forever()
+```
+
+Run it somehow. Perhaps as a `Type=simple` systemd service.
+
+And nginx config fragment, which should be dropped inside the `http{}` block.
+A very standard reverse proxy setup.
+Unrestricted GET, and basic_auth protected POST (and everything else) for updating the redirect mappings.
+
+```conf
+upstream sh {
+ server 127.0.233.80:8046;
+ keepalive 2;
+}
+
+server {
+ listen 443 ssl;
+ listen [::]:443 ssl;
+ server_name sh.example.com;
+
+ location / {
+ limit_except GET {
+ auth_basic "POST restricted";
+ auth_basic_user_file /etc/nginx/sh.example.com-htpasswd;
+ }
+
+ proxy_pass http://sh;
+ }
+}
+```
+
+
+And update our port mapping script to send the newly obtained port to our redirects server.
+
+```diff
+--- a/protonvpn-update-port-mapping.sh
++++ b/protonvpn-update-port-mapping.sh
+@@ -8,6 +8,9 @@
+ ip6=$(curl -s --interface=protonvpn -6 icanhazip.com)
+ echo "$ip6" > "$RUNTIME_DIRECTORY/public-ip6"
+
++SERVICE_NAME=funny-business
++
++old_port=0
+ while true; do
+ natpmpc -a 1 22 udp 60 -g 10.2.0.1 > /dev/null \
+ && natpmpc -a 1 22 tcp 60 -g 10.2.0.1 > $TMPFILE \
+@@ -21,5 +24,14 @@
+
+ echo "$port" > "$RUNTIME_DIRECTORY/public-port"
+
++ if [[ $port != $old_port ]]; then
++ echo "Port changed from $old_port to $port"
++ # Valid DNS names and digits don't need any URL encoding to be standard compliant, so I'm using -d to be short
++ curl -X POST -u 'YOUR_USER:YOUR_PASSWORD' https://sh.example.com/-/config \
++ -d "prefix=$SERVICE_NAME" \
++ -d "port=$port"
++ old_port=$port
++ fi
++
+ sleep 45
+ done
+```
+
+Start everything, and now you can visit [https://sh.example.com/funny-business] for fun and profit.