# Reverse proxy to Tor
# Advantages
- no port forwarding needed on the LAN of the host
- encrypted connection
- hides the IP of the host
# Requirements
- a Virtual Private Server (VPS) - eg. a minimal package on Lunanode for ~3.5$/month
- root access on the VPS - you need to set up webserver and install packages
- a domain or subdomain - this will be setup on the proxy webserver
Get the Tor .onion
address of your BTCPay Server via the Server settings > Services
page.
See information in the "HTTP-based TOR hidden services" section.
Note: There is also a Docker version of this setup.
# VPS Setup
You will create a nginx reverse proxy and a socat
service, which forwards requests to your BTCPay Server.
Login as root and install the required dependencies: (example assumes a Debian based system)
# switch to root user (if not logged in as root)
sudo su -
# install dependencies
apt update
apt install -y certbot nginx socat tor
# Socat setup
Create the service file /etc/systemd/system/http-to-socks-proxy@.service
:
[Unit]
Description=HTTP-to-SOCKS proxy
After=network.target
[Service]
EnvironmentFile=/etc/http-to-socks-proxy/%i.conf
ExecStart=/usr/bin/socat tcp4-LISTEN:${LOCAL_PORT},reuseaddr,fork,keepalive,bind=127.0.0.1 SOCKS4A:${PROXY_HOST}:${REMOTE_HOST}:${REMOTE_PORT},socksport=${PROXY_PORT}
[Install]
WantedBy=multi-user.target
Create the configuration for the service in /etc/http-to-socks-proxy/btcpayserver.conf
:
# create the directory
mkdir -p /etc/http-to-socks-proxy/
# create the file with the content below
nano /etc/http-to-socks-proxy/btcpayserver.conf
Replace the REMOTE_HOST
and adapt the ports if needed:
PROXY_HOST=127.0.0.1
PROXY_PORT=9050
LOCAL_PORT=9081
REMOTE_HOST=heregoesthebtcpayserverhiddenserviceaddress.onion
REMOTE_PORT=80
Create a symlink in /etc/systemd/system/multi-user.target.wants
to enable the service and start it:
# enable
ln -s /etc/systemd/system/http-to-socks-proxy\@.service /etc/systemd/system/multi-user.target.wants/http-to-socks-proxy\@btcpayserver.service
# start
systemctl start http-to-socks-proxy@btcpayserver
# check service status
systemctl status http-to-socks-proxy@btcpayserver
# check if tunnel is active
netstat -tulpn | grep socat
# should give something like this:
# tcp 0 0 127.0.0.1:9081 0.0.0.0:* LISTEN 951/socat
# Webserver setup
# Point domain to the VPS
Create the A record on the DNS server of your domain/subdomain and point it to your VPS IP address.
# Prepare SSL and Let's Encrypt
# generate 4096 bit DH params to strengthen the security, may take a while
openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096
# create directory for Let's Encrypt files
mkdir -p /var/lib/letsencrypt/.well-known
chgrp www-data /var/lib/letsencrypt
chmod g+s /var/lib/letsencrypt
# nginx configuration: http
Create a variable mapping to forward the correct protocol setting and check if the Upgrade header is sent by the client, e.g. /etc/nginx/conf.d/map.conf
:
map $http_x_forwarded_proto $proxy_x_forwarded_proto {
default $http_x_forwarded_proto;
'' $scheme;
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
Create a config file for the domain, e.g. /etc/nginx/sites-available/btcpayserver.conf
:
server {
listen 80;
server_name btcpayserver.mydomain.com;
# Let's Encrypt verification requests
location ^~ /.well-known/acme-challenge/ {
allow all;
root /var/lib/letsencrypt/;
default_type "text/plain";
try_files $uri =404;
}
# Redirect everything else to https
location / {
return 301 https://$server_name$request_uri;
}
}
We will configure the https server part in the same config file once we obtained the SSL certificate.
Enable the web server config by creating a symlink and restarting nginx:
ln -s /etc/nginx/sites-available/btcpayserver.conf /etc/nginx/sites-enabled/btcpayserver.conf
systemctl restart nginx
# Obtain SSL certificate via Let's Encrypt
Run the following command with adapted email and domain parameters:
certbot certonly --agree-tos --email admin@mydomain.com --webroot -w /var/lib/letsencrypt/ -d btcpayserver.mydomain.com
# nginx configuration: https
Now that we have a valid SSL certificate, add the https server part at the end of /etc/nginx/sites-available/btcpayserver.conf
:
server {
listen 443 ssl http2;
server_name btcpayserver.mydomain.com;
# SSL settings
ssl_stapling on;
ssl_stapling_verify on;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
# Update this with the path of your certificate files
ssl_certificate /etc/letsencrypt/live/btcpayserver.mydomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/btcpayserver.mydomain.com/privkey.pem;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 30s;
add_header Strict-Transport-Security "max-age=63072000" always;
add_header Content-Security-Policy "frame-ancestors 'self';";
add_header X-Content-Type-Options nosniff;
# Proxy requests to the socat service
location / {
proxy_pass http://127.0.0.1:9081/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
Restart nginx once more:
systemctl restart nginx
Now, visiting btcpayserver.mydomain.com
should show your BTCPay Server instance.
TIP
If you see an nginx error of "503 Service Temporarily Unavailable" or similar but your BTCPay Server is reachable otherwise, you need to make BTCPay Server aware of your new domain. You can do so by using environment variables (Docker based setup), log into your BTCPay Server via SSH:
sudo su -
cd $BTCPAY_BASE_DIRECTORY/btcpayserver-docker/
export BTCPAY_ADDITIONAL_HOSTS="btcpayserver.mydomain.com"
. ./btcpay-setup.sh -i
# Do all this in a Docker container
Ready made Docker image (opens new window) (Code (opens new window))
# SocaTor = SOCAT + TOR
Based on Docker-Socator (opens new window)
It uses socat to listen on a given TCP port (5000 in this example) and to redirect incoming traffic to a Tor hidden service specified through environment variables.
It acts as a relay between the standard web and a hidden service on the Tor network.
You can optionally restrict the IP addresses that are allowed to connect to this service by specifying an ALLOWED_RANGE
environment variable and using CIDR notation.
Please note:
This container does not have any nginx component because Kubernetes provides for it.
# Usage
Break free from cloud services providers limitations, secure and protect your bitcoin full node, connect that with a BTC Pay server, all behind TOR. Selectively expose the BTCPay Server payment gateway and API to clearnet using socat+tor running on the Internet.
# Build
docker build -t cloudgenius/socator .
# Push
docker push cloudgenius/socator
# Start the image in background (daemon mode) with IP address restriction
docker run -d \
-p 5000:5000 \
-e "ALLOWED_RANGE=192.168.1.0/24" \
-e "TOR_SITE=zqktlwiuavvvqqt4ybvgvi7tyo4hjl5xgfuvpdf6otjiycgwqbym2qad.onion" \
-e "TOR_SITE_PORT=80" \
--name socator \
cloudgenius/socator
# Start the image in foreground
docker run --rm -ti \
-p 5000:5000 \
-e "TOR_SITE=zqktlwiuavvvqqt4ybvgvi7tyo4hjl5xgfuvpdf6otjiycgwqbym2qad.onion" \
-e "TOR_SITE_PORT=80" \
--name socator \
cloudgenius/socator
Now http://localhost:5000
should show you the tor hidden service you specified in the above command.
# Use that Docker container in a Kubernetes Cluster using these manifests
These manifest assumes a typical Kubernetes cluster that exposes internal services (like socator running internallly at port 5000) to the clearnet/public internet via Nginx Ingress (opens new window) and provide automated Let's Encrypt TLS/SSL certificates via cert-manager (opens new window).
Deployment manifest
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: socator
spec:
replicas: 1
selector:
matchLabels:
role: socator
template:
metadata:
labels:
role: socator
spec:
containers:
- image: cloudgenius/socator # code https://github.com/beacloudgenius/socator
imagePullPolicy: IfNotPresent
name: socator
env:
- name: TOR_SITE
value: 'zqktlwiuavvvqqt4ybvgvi7tyo4hjl5xgfuvpdf6otjiycgwqbym2qad.onion' # BTCPay Server Tor address => docker exec tor cat /var/lib/tor/app-btcpay-server/hostname
- name: TOR_SITE_PORT
value: '80'
Service manifest
---
apiVersion: v1
kind: Service
metadata:
name: socator
spec:
ports:
- name: http
port: 5000
selector:
role: socator
Ingress manifest
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: socator
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
rules:
- host: btcpayserver.mydomain.com
http:
paths:
- backend:
service:
name: socator
port:
number: 5000
path: /
pathType: Prefix
tls:
- hosts:
- btcpayserver.mydomain.com
secretName: socator-tls
# Resources
- nginx reverse proxy to .onion site in Tor network (opens new window)
- Tor-to-IP tunnel service (opens new window)
- How to make a nginx reverse proxy direct to tor hidden service (opens new window)
- Secure Nginx with Let's Encrypt on Debian 10 Linux (opens new window)
- Nginx WebSocket proxying (opens new window)