Skip to content

Expose your local HTTP service to a public HTTPS service using Caddy and an SSH tunnel

License

Notifications You must be signed in to change notification settings

joostd/https-reverse-proxy-over-ssh

Repository files navigation

https-reverse-proxy-over-ssh

Expose your local HTTP service to a public HTTPS service using Caddy and an SSH tunnel.

Introduction

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

  1. terminate HTTPS connections from the Internet using Let's Encrypt certificates generated on-the-fly,
  2. reverse proxy those connections to a local service,
  3. 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

Install

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.

Install using ansible

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

Use

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).

Disabling shell accounts

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.

Implementation

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.

Security

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.

Troubleshooting

See Caddy logs:

journalctl --no-pager -u caddy

Reset Caddy to default config:

systemctl reload caddy

TO DO

  • replace homepage in /var/www/html with user instructions
  • add instructions with SSH certificates
  • use virtual hosts insteead of port numbers

About

Expose your local HTTP service to a public HTTPS service using Caddy and an SSH tunnel

Resources

License

Stars

Watchers

Forks

Packages

No packages published