No NAT loopback / DuckDNS / NGINX / AdGuard

I’ve recently moved to a new ISP, received a new router which unfortunately does not support NAT loopback. Concretely, this means that when I set up DuckDNS to access my Home Assistant instance from an external network, I cannot use the same DuckDNS URL when on my LAN but I need to use my Raspberry Pi’s (on which I’ve set up Home Assistant Container) internal IP.

So, the as-is situation is as follows:

Accessing HA from LAN: https://192.168.x.x:8123
Accessing HA from external network: https://xxx.duckdns.org

This is what I want the to-be solution to be:

Accessing HA from LAN: https://xxx.duckdns.org
Accessing HA from external network: https://xxx.duckdns.org

I’ve been struggling with this a couple of days now, doing a lot of research and reading several articles. At this stage, I am very close to get it to work. I’ve used the following sources for setting everything up so far:

  • DuckDNS setup (followed this guide from start to end)
  • Split Brain DNS (in this section they talk specifically about routers which do not support NAT loopback / hairpin NAT, so I’ve followed these instructions + the instructions about reverse proxy with NGINX)
  • Reverse Proxy with NGINX (I’ve read that a solution for routers which do not support NAT loopback is to use a reverse proxy)
  • NGINX Proxy Manager (Followed these instructions to set up NGINX Proxy Manager on my Raspberry Pi as well, and created a “Proxy Host”, see below)

NGINX Proxy Manager setup (I’ve also configured the SSL tab):

DNS Rewrite defined in AdGuard home:

My HA configuration.yml:

homeassistant:
  external_url: https://xxx.duckdns.org

# Access HA externally through DuckDNS
duckdns:
  domain: xxx.duckdns.org
  access_token: 86a047fb-xxxx-xxxx-xxxx-xxxxxxxxxxxx

# TLS/SSL certificates for external access
http:
  # base_url: https://xxx.duckdns.org:8123  # Tried with and without base_url
  use_x_forwarded_for: true
  trusted_proxies:
    - 172.30.33.0/24            # Docker NGINX
    - 192.168.x.x                 # My Raspberry Pi IP
    - 172.30.32.0/23            # In Home Assistant we need to add the Docker subnet
    - 127.0.0.1                     # Add the localhost IPv4 address
    - ::1                                # Add the localhost IPv6 address
  # ssl_certificate: /etc/letsencrypt/live/xxx.duckdns.org/fullchain.pem   # Not needed anymore when NGINX is set up
  # ssl_key: /etc/letsencrypt/live/xxx.duckdns.org/privkey.pem

The port forwards on my router are 80 → 80 and 443 → 443 for my Raspberry Pi internal IP 192.168.x.xxx.

To test, I’ve manually changed the DNS settings of my WiFi on my phone to point to my Raspberry Pi internal IP (which is also where AdGuard is running on and the above mentioned DNS Rewrite is defined). Using AdGuard as my DNS server, I can access my HA instance on my LAN through the following URLs:

https://xxx.duckdns.org
http://192.168.x.x:8123

So far the good news. Externally, I still cannot access my HA instance using the DuckDNS URL, I get an error ERR_SSL_PROTOCOL_ERROR.

HOWEVER, when I change the port forward 443 → 443 to, for example, 24029 → 443 (just a random port), I CAN access my HA instance externally through:

https://xxx.duckdns.org:24029

And internally through:

https://xxx.duckdns.org

For some reason, externally it won’t work with port 443 and my DuckDNS URL without any ports at the end. However, as stated at the start of this topic, ideally I would like for both my internal and external URL to be the same, i.e. https://xxx.duckdns.org.

Everything is running on my Raspberry Pi on Docker (HA, AdGuard and NGINX Proxy Manager). These are the docker run statements I used:

Home Assistant Container

docker run --init -d \
    --name homeassistant  \
    --restart=unless-stopped  \
    -v /etc/localtime:/etc/localtime:ro  \
    -v /home/pi/Documents/home-assistant:/config  \
    -v /etc/letsencrypt:/etc/letsencrypt \
    --network=host --privileged \
    homeassistant/raspberrypi3-homeassistant:latest

AdGuard Home

docker run --name adguardhome\
    --restart unless-stopped\
    -v /home/pi/Documents/adguardhome/work:/opt/adguardhome/work\
    -v /home/pi/Documents/adguardhome/conf:/opt/adguardhome/conf\
    -p 53:53/tcp -p 53:53/udp\
    -p 3000:3000/tcp\
    -p 853:853/tcp\
    -p 784:784/udp -p 853:853/udp -p 8853:8853/udp\
    -p 5443:5443/tcp -p 5443:5443/udp\
    -d adguard/adguardhome

NGINX Proxy Manager

docker run --name nginxproxymanager \
    --restart unless-stopped \
    -v /data:/data \
    -v /etc/letsencrypt:/etc/letsencrypt \
    -p 81:81 \
    jc21/nginx-proxy-manager:latest

I feel like I’m very close and I’m hoping someone can just tell me what I’m doing wrong. I feel like I’m mixing settings from all these guides I’ve read. Much appreciated!

Ok so apparently my ISP, by default, blocked a couple of ports: 23, 80 and 443. It was a setting I could change in my subscription settings and after that everything worked fine!