NPM: How to keep and maintain a dynamic IP (like your public IP) in an access list. reddit / selfhosted ---------------------------------------------------------------------- BillGoats · 2023-11-20 13:23 # Background I've made a lot of changes to my setup recently, and one that made something like this necessary. Specifically, I'm now self-hosting DNS (using Technitium). In it, I've created a zone for my domain (e.g. example.com) with an A record for subdomains (*.example.com) pointing to the local IP of my server. This does 2 pretty cool things. 1) When I'm at home, requests to my public subdomains goes directly to my server instead of via CloudFlare. 2) I can set up subdomains with SSL that are only available from my home network (using access lists in NPM). # Problem In NPM, the access list for local access includes my local subnet. That works - but when I'm connected to my local network via WireGuard (FireZone), the IP that is seen by the services I connect to is my public IP. This changes quite a lot, and my ISP doesn't offer static IPs for private users. # Research I started looking into how to make add dynamic IPs to NPM access lists. I came across a couple of GitHub issues ([1](https://github.com/NginxProxyManager/nginx-proxy- manager/issues/1708#issuecomment-1537533615), [2](https://github.com/NginxProxyManager/nginx-proxy- manager/issues/2240)) on the topic. It looks like people have solved the problem, but not in a complete way without modifying the NPM docker image. I did not want to do that, so decided looking into writing a separate script. # Solution What I ended up with is available in this [gist](https://gist.github.c om/RonnyAL/c428831fc08145e8890a8f390b237ef5). Usage: `update-public-ip-acl.sh -i 1 -p /home/BillGoats/docker/npm/data -d npm-app` The `-p` parameter points to NPMs database (`database.sqlite`). The `-i` is the ID of the database entry you want to change, and `-d` is the name of the NPM docker container. **The script assumes that you have already added an entry that you want to maintain.** I guess one could make the script smarter, but I found that easiest to implement. So, to get started: Add your current public IP (or some random IP if you want to verify that this works) to an ACL. Then, to get the ID of this entry, execute: `sudo sqlite3 "/path/to/database.sqlite" "select id from access_list_client where address = '';"` This should output the ID you need to execute the script. What the script actually does is the following: 1. Check currently configured IP. 2. Check current public IP. * If they are the same, exit with code 0. * If they are not the same, proceed. 3. Replace the old IP with the new one in the NPM database. 4. Replace the old IP wth the new one in `/data/nginx/proxy_host/.conf`. 5. Execute `nginx -s reload` in the NPM container. # Conclusions During testing, this seems to have worked fine. I did notice however that if I modified the access list manually, the ID of the underlying entry disappeared from the database and was replaced by a new row. There's probably room for improvement in general. Suggestions are highly welcome. Hopefully this is enough to get others in the same boat a little further. ---------------------------------------------------------------------- ElevenNotes · 2023-11-20 13:37 Your proxy should not see your public IPv4 when you connect via Wireguard but the private IPv4 of the Wireguard tunnel, because that’s the IP that makes the connection, not the public IPv4. If you see the public IPv4 you are not connecting to your proxy via Wireguard but via WAN. ---------------------------------------------------------------------- BillGoats · 2023-11-20 13:59 Oops. I actually thought that's how it should work (so found it weird that I even needed a rule for my public IP). I must have misconfigured something somewhere if that's how it's supposed to work. I'll look into how to fix that. Meanwhile, maybe the script is useful for someone else, so I'll leave it up. Thanks! ---------------------------------------------------------------------- root-node · 2023-11-20 14:30 While your IPv4 regex is basic enough to pass, it's not valid for actual IP addresses. Using your method, `999.999.999.999` is valid. This would be a better option, while keeping it short: `^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$` ---------------------------------------------------------------------- BillGoats · 2023-11-20 15:50 Thanks. For one-off regex patterns I tend to just google examples and pick one from StackOverflow, lol. But you're right, it's not the best option! ---------------------------------------------------------------------- BillGoats · 2023-11-20 16:15 Commenting again after looking a little into it. I might make a new post about this, but I had a thought... So locally, my DNS points (say) `secure.example.com` to a local IP. Meanwhile, CloudFlare points `*.example.com` to my public IP, and my router points subdomain requests to my server (with that local IP). I actually have a separate A record for `firezone.example.com` that points to my public IP because unless I'm mistaken, the free tier of CloudFlare will only proxy traffic on ports 80 and 443. In NPM, I'm using a custom advanced configuration that includes a bunch of `set_real_ip_from`lines with CloudFlare IP ranges as well as the line `real_ip_header CF-Connecting-IP`. This allows me to see actual connection IPs instead of CloudFlare's IPs. **This is the part that I find confusing.** When I connect to my network via FireZone, I can visit local IPs without issue. And so I assumed that traffic was properly proxied through the WireGuard tunnel. When I instead use `secure.example.com`, this traffic comes from my public IP. Does this mean that DNS queries ignore the local router settings and end up sending traffic to my public IP? ---------------------------------------------------------------------- ElevenNotes · 2023-11-20 16:36 This is correct. In order for the access to come from the VPN client, the VPN client needs to resolve the FQDN to the internal IP of your proxy. To achieve this, your VPN client needs to use a DNS server that resolves it this way, aka your internal DNS servers at home. Orherwise it will resolve to your external address and access will come from WAN. ---------------------------------------------------------------------- BillGoats · 2023-11-20 16:38 Right! It all makes sense when I take a step back. Thanks for giving me the push :) ---------------------------------------------------------------------- ElevenNotes · 2023-11-20 16:39 You're welcome, always glad to be of help.