Forward various domains to specific internal hosts with single public ip

Forgive me if this is basic knowledge, I have searched the documentation and forum but was not able to find a clear answer (and I don't really know which terms to search for).

I have a couple of Raspberry Pis running various services behind a LEDE router.

I have set up DDNS and can forward ports, eg. port 80 to host a webserver on one of the RPIs. But if I do that I have to use a non-standard port to be able to host (or rather access) a secondary webserver on the other RPI, which is not desirable.

What is the best way to forward wan traffic to individual lan hosts based on requested domain in LEDE? I only have one (dynamic) public ip.

Eg. Both rpi1.ddns.com and rpi2.ddns.com resolves to the same public ip (the LEDE router). When a user requests http://rpi1.ddns.com from wan traffic is forwarded to 192.168.1.2 in lan, but when a user requests http://rpi2.ddns.com from wan traffic is forwarded to 192.168.1.3 in lan.

Thank you all in advance :slight_smile:

I think it can be done using haproxy (available through opkg) as a reverse proxy, but I haven't looked into it more closely myself.

Thanks a lot @Per, you were right again.

I was not sure whether I needed to look at DNS and dnsmasq settings and I was not keen on running Apache or NginX on a router with limited resources.

But like you suggested, I can now served up to 5 domains (per account) with Duckdns and HAproxy behind my LEDE router with a single, dynamic and public IP.

I had to combine a few guides to get both http and https traffic to work. I can share my config, if anyone is interested.

Thanks. I am interested in the config. Especially with https.

Is there really need for haproxy?

https://httpd.apache.org/docs/2.4/vhosts/examples.html

Nope, "SOP" with nginx as well.

Been a while since I've run Apache, but I assume it also handles with multi-domain certificates, allowing not only a single server to provide multiple hosts/domains, but with TLS as well.

Squid would also be a good choice to handle this kind of thing as well I suspect.

I think you both missed my point. I do use name-based hosts in NginX, but I do not just have a single machine serving multiple hosts.

Instead I have multiple machines serving multiple hosts all with a single, public IP. That is why I need a proxy service on the LEDE router, that first determines which machine is serving the particular name-based host, before the individual NginX web servers are able to serve the right content based on the URL.

@cilgo I will try to clean up my config and post it here.

@dlakelan I'm not sure Squid will handle SSL passthrough, but HAproxy does :slight_smile:

Even easier as you've already got nginx deployed. Just "pick one" instance, forward to that instance, and reverse proxy from there. Define your TLS parameters and certs at the instance level. Your choice as to if you want to use TLS on the connections to the proxied servers.

No need to run anything on OpenWRT other than the port forward.

    server {
        listen          443 ssl http2;
        server_name     three.example.com;
        location / {
            include     local/proxy_set_header;
            add_header  Strict-Transport-Security
                        "max-age=31536000; includeSubDomains"
                        always;
            proxy_pass  http://internal.server3.example.com/;
        }
    }

/usr/local/etc/nginx/local/proxy_set_header

# Standard proxy_set_header declarations

proxy_set_header        Host $host;
proxy_set_header        X-Real-IP $remote_addr;
proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header        X-Forwarded-Proto $scheme;

If you use Let's Encrypt and certbot, you may find it helpful to add to each definition (I do it as an include)

# Don't proxy /.well-known/acme-challenge/

location /.well-known/acme-challenge/ {
    index off;
    alias /path/to/some/safe/www/writable/place/.well-known/acme-challenge/;
}

Yes that would also work @jeff, but the whole point of this is not having to rely on one server to proxy traffic to another. The Raspberry Pi is a great little machine, but it is neither very powerful nor very stable (at least if you like to tinker around with them, like I do).
That is why I like to use the spare resources on my LEDE router to handle the proxy selection, so one rpi-server can go offline (accidentally or intentionally) without affecting the other.

I'm sorry for the wait @cilgo, but here is the config:

# Global parameters
global

    # Specifiy the maximum number of allowed connections.
    maxconn 32000

    # Raise the ulimit for the maximum allowed number of open socket
    # descriptors per process. This is usually at least twice the
    # number of allowed connections (maxconn * 2 + nb_servers + 1) .
    ulimit-n 65535

    # Drop privileges (setuid, setgid), default is "root" on OpenWrt.
    uid 0
    gid 0

    # Daemonize on startup
    daemon

    nosplice
    # Enable debugging
    #debug

    # Spawn given number of processes and distribute load among them,
    # used for multi-core environments or to circumvent per-process
    # limits like number of open file descriptors. Default is 1.
    #nbproc 2

    # SSL options
    ssl-default-bind-options force-tlsv12
    ssl-default-bind-ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256
    ssl-default-server-options force-tlsv12
    ssl-default-server-ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256

frontend localhost80  #Front end for port 80
    #Switched to port 81 and redirect wan:80 to lan:81 to allow luci-app-acme to use port 80
    bind *:81 transparent
    timeout client 30000
    mode http
    #redirect scheme https code 301 if !{ ssl_fc }

    # Define hosts
    acl host_url180 hdr(host) -i url1.example.com   # URL server1.1
    acl host_url280 hdr(host) -i url2.example.com   # URL server1.2
    acl host_url380 hdr(host) -i url3.example.com   # URL server2.1
    acl host_url480 hdr(host) -i url4.example.com   # URL server2.2
    acl host_url580 hdr(host) -i url5.example.com   # URL LEDE router
    acl host_lede80 hdr(host) -i lede.lan
    acl host_uhttpd80 hdr(host) -i 192.168.1.1

    ## figure out which one to use
    use_backend server180 if host_url180
    use_backend server180 if host_url280
    use_backend server280 if host_url380
    use_backend server280 if host_url480
    use_backend uhttpd80 if host_url580
    use_backend uhttpd80 if host_lede80
    use_backend uhttpd80 if host_uhttpd80

frontend localhost443   #Listens on port 443
    #Switched to port 444 and redirect wan:443 to lan:444 to allow luci-app-acme to use port 443
    bind *:444 transparent
    timeout client 30000
    #option tcplog
    mode tcp

    acl sslv3 req.ssl_ver 3
    acl tls req.ssl_hello_type 1

    tcp-request inspect-delay 5s
    tcp-request content accept if tls !sslv3

    # Define hosts
    acl host_url1443 req.ssl_sni -i url1.example.com   # URL server1.1
    acl host_url2443 req.ssl_sni -i url2.example.com   # URL server1.2
    acl host_url3443 req.ssl_sni -i url3.example.com   # URL server2.1
    acl host_url4443 req.ssl_sni -i url4.example.com   # URL server2.2
    acl host_url5443 req.ssl_sni -i url5.example.com   # URL LEDE router
    acl host_lede443 req.ssl_sni -i lede.lan   # ACL LEDE router
    acl host_uhttpd443 req.ssl_sni -i LEDE   # ACL LEDE router

    ## figure out which one to use
    use_backend server1443 if host_url1443
    use_backend server1443 if host_url2443
    use_backend server2443 if host_url3443
    use_backend server2443 if host_url4443
    use_backend uhttpd443 if hhost_url5443
    use_backend uhttpd443 if host_lede443
    use_backend uhttpd443 if host_uhttpd443

backend server180
    mode http

    option httpchk

    timeout connect 5000
    timeout server 30000

    server server180 192.168.2.2:8080 send-proxy
    # send proxy protocol to port 8080 to distinguish wan proxy http traffic from lan http traffic.
    # 
    # Configure NginX with:
    #
    # http {
    # ...
    # 
    # server {
    #    listen 80 default_server;
    #    listen 8080 proxy_protocol default_server;
    #
    #    server_name url#.example.com;
    # }
    
backend server1443
    mode tcp

    timeout connect 5000
    timeout server 30000

    # maximum SSL session ID length is 32 bytes.
    stick-table type binary len 32 size 30k expire 30m

    acl clienthello req_ssl_hello_type 1
    acl serverhello rep_ssl_hello_type 2

    # use tcp content accepts to detects ssl client and server hello.
    tcp-request inspect-delay 5s
    tcp-request content accept if clienthello

    # no timeout on response inspect delay by default.
    tcp-response content accept if serverhello

    stick on payload_lv(43,1) if clienthello

    # Learn on response if server hello.
    stick store-response payload_lv(43,1) if serverhello

    option ssl-hello-chk

    server server1443 192.168.2.2:8443 send-proxy
    # send proxy protocol to port 8443 to distinguish wan proxy http traffic from lan http traffic.
    # 
    # Configure NginX with:
    #
    # server {
    #    listen 443 ssl http2 default_server;
    #    listen 8443 ssl http2 proxy_protocol default_server;
    #
    #    server_name url#.example.com;
    # }

backend server280
    mode http

    option httpchk

    timeout connect 5000
    timeout server 30000

    server server280 192.168.2.3:8080 send-proxy
    # send proxy protocol to port 8080 to distinguish wan proxy http traffic from lan http traffic.
    # 
    # Configure NginX with:
    #
    # server {
    #    listen 80 default_server;
    #    listen 8080 proxy_protocol default_server;
    #
    #    server_name url#.example.com;
    # }
    
backend server2443
    mode tcp

    timeout connect 3600000
    timeout server 3600000
	timeout client 3600000

    # maximum SSL session ID length is 32 bytes.
    stick-table type binary len 32 size 30k expire 30m

    acl clienthello req_ssl_hello_type 1
    acl serverhello rep_ssl_hello_type 2

    # use tcp content accepts to detects ssl client and server hello.
    tcp-request inspect-delay 5s
    tcp-request content accept if clienthello

    # no timeout on response inspect delay by default.
    tcp-response content accept if serverhello

    stick on payload_lv(43,1) if clienthello

    # Learn on response if server hello.
    stick store-response payload_lv(43,1) if serverhello

    option ssl-hello-chk

    server server2443 192.168.2.3:8443 send-proxy
    # send proxy protocol to port 8080 to distinguish wan proxy http traffic from lan http traffic.
    # 
    # Configure NginX with:
    #
    # server {
    #    listen 443 ssl http2 default_server;
    #    listen 8443 ssl http2 proxy_protocol default_server;
    #
    #    server_name url#.example.com;
    # }
    
backend uhttpd80
    mode http

    option httpchk

    timeout connect 5000
    timeout server 120000

    server uhttpd80 192.168.2.1:80

backend uhttpd443
    mode tcp

    timeout connect 5000
    timeout server 120000

    # maximum SSL session ID length is 32 bytes.
    stick-table type binary len 32 size 30k expire 30m

    acl clienthello req_ssl_hello_type 1
    acl serverhello rep_ssl_hello_type 2

    # use tcp content accepts to detects ssl client and server hello.
    tcp-request inspect-delay 5s
    tcp-request content accept if clienthello

    # no timeout on response inspect delay by default.
    tcp-response content accept if serverhello

    stick on payload_lv(43,1) if clienthello

    # Learn on response if server hello.
    stick store-response payload_lv(43,1) if serverhello

    option ssl-hello-chk

    server uhttpd443 192.168.2.1:443 check