Expose your local HTTP service to a public HTTPS service using Caddy and an SSH tunnel.
This is a simple implementation of an HTTPS reverse proxy for a web service running on your local system. It functions similar to popular services like ngrok or localhost.run
The difference with these services:
- this proxy runs on a server of your own, adding privacy and control over domain names and port numbers.
- this proxy is typically used by members of a dev team (I've created this to easily test the tiqr app).
HTTPS server certificates are generated automatically using Let's Encrypt.
In a nutshell, all this script does is
- terminate HTTPS connections from the Internet using Let's Encrypt certificates generated on-the-fly,
- reverse proxy those connections to a local service,
- which is routed over an SSH tunnel back to the SSH client
Similar to:
ssh proxy.example.org -l ubuntu -R 1234:localhost:8080 caddy reverse-proxy --from https://proxy.example.org:4443 --to localhost:1234
This reverse proxy can be deployed on any server, assuming ubuntu:
Install jq:
apt install jq
Install Caddy:
echo "deb [trusted=yes] https://apt.fury.io/caddy/ /" | sudo tee -a /etc/apt/sources.list.d/caddy-fury.list
sudo apt update
sudo apt install caddy
Install Caddy config file:
sudo cp Caddyfile.example /etc/caddy/Caddyfile
Replace the first line in that file with the fully qualified domain name of your reverse proxy.
Create the reverse proxy config file:
cp config.example config
Replace the HOST variable in that file with the fully qualified domain name of your reverse proxy.
Create an ansible inventory
file with the name of your proxy.
Then run the ansible playbook using:
ansible-playbook -i inventory ansible/playbook.yml
If needed, you can specify the SSH key to use with something like --key-file "~/.ssh/rsa_id.pem"
Add user ubuntu
to group caddy
:
adduser ubuntu caddy
Start a local web service, for instance using Caddy on your local system:
caddy file-server -browse -listen :8004
Or using PHP's builtin web server:
echo '<?php phpinfo();' > /tmp/index.php
php -S 0:8001 /tmp/index.php
Then, open a tunnel to the proxy with the necessary plumbing:
ssh proxy.example.org -l ubuntu -i ~/.ssh/id_rsa.pem -R 8001:localhost:8001 -t ./reverse-proxy.sh 1
The script argument is simply an offset to the base port numbers used (4000 on the proxy, 8000 on the client).
After the proxy server has starter, the script continues to display proxy access logs. You can press ^C to terminate the proxy (and the SSH connection if you don't use terminal mode).
A shell account is not needed to run the proxy, if you restrict users to using SSH command mode.
This can be done through SSH configuration in a user's authorized_keys
file.
You can also choose to embed the reverse-proxy script in issued SSH certificates.
That way you can grant access to the reverse proxy service by distributing SSH certificates.
Upon a succesful SSH connection, a Caddy configuration is generated and loaded, similar to:
https://proxy.example.org {
reverse_proxy :8001
log {
output file /tmp/access.log
format single_field common_log
}
}
The local service on port 8001 is forwarded over the SSH tunnel to the SSH client.
Note that when connecting to your proxy, anyone on the Internet can connect to your local web service. Use a firewall on your proxy to limit web clients.
Also make sure you don't accidentally expose a service by proxying a port on which another service is already listening.
See Caddy logs:
journalctl --no-pager -u caddy
Reset Caddy to default config:
systemctl reload caddy
- replace homepage in /var/www/html with user instructions
- add instructions with SSH certificates
- use virtual hosts insteead of port numbers