NPM: How to keep and maintain a dynamic IP (like your public IP) in an access list. - eviltoast

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, 2) 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. 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.
  1. Replace the old IP with the new one in the NPM database.
  2. Replace the old IP wth the new one in /data/nginx/proxy_host/.conf.
  3. 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.

Edit: Forgot to mention that the idea is to run this on a cron schedule (say every 5 minutes).

  • root-node@alien.topB
    link
    fedilink
    English
    arrow-up
    1
    ·
    1 year ago

    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@alien.topOPB
      link
      fedilink
      English
      arrow-up
      1
      ·
      1 year ago

      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!