How To Allow Site Access In Nginx By DDNS Instead Of By IP



  • In a previous post, I was looking for a way to allow access to a website behind a Nginx proxy based on a dynamic DNS domain. I had already set the allow/deny statements in the config file for the IP ranges assigned to the company, now I just needed a way to also allow access for the CEO from home when he has a dynamic IP.

    The beauty of this, for this setup, is that I can allow access from within the config file "location" and this could be different for each of the domains configured on this Nginx instance.

    Btw, credit where credit is due.
    https://blog.zencoffee.org/2013/12/dynamic-dns-filtering-nginx/

    First, here is the config file before with the allow/deny rules for the IP ranges. This company has 2 sets of assigned IP ranges.

    server {
       listen 80;
       server_name domain.ca;
       return 301 https://$server_name$request_uri;
    }
    
    server {
      listen 443 ssl http2;
      server_name domain.ca;
      
      add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
      add_header X-XSS-Protection "1; mode=block";
      add_header X-Content-Type-Options nosniff;
      add_header Referrer-Policy strict-origin;
      add_header Content-Security-Policy "default-src" always;
      add_header X-Frame-Options SAMEORIGIN;
      ssl_stapling on;
      ssl_stapling_verify on;
      server_tokens off;
    
      ssl on;
      ssl_certificate /etc/letsencrypt/live/domain.ca/fullchain.pem;
      ssl_certificate_key /etc/letsencrypt/live/domain.ca/privkey.pem;
      ssl_session_timeout 5m;
      ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
      ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
      ssl_prefer_server_ciphers on;
      ssl_session_cache shared:SSL:10m;
      ssl_dhparam /etc/ssl/certs/dhparam.pem;
      proxy_cookie_path / "/; secure; HttpOnly";
    
    
        location / {
        	allow 192.168.1.0/24; #obviously not the real IP range but represents IP range 1
            allow 192.168.2.0/24; #obviously not the real IP range but represents IP range 2
            deny all; #deny all other IPs
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $http_host;
            proxy_set_header X-NginX-Proxy true;
            proxy_pass http://192.168.100.61;
            proxy_redirect off;
    
            # Socket.IO Support
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
       }
    }
    

    Step 1:
    I created a new file at /etc/cron.daily/ and named it getddns

    Step 2
    Added this code to the new file

    #!/bin/bash
    host mydynamicdomain.ddns.net | grep "has address" | sed 's/.*has address //' | awk '{print "allow\t\t" $1 ";\t\t# DDNS IP" }' > /etc/nginx/conf.d/homeip.inc
    service nginx reload > /dev/null 2>&1
    

    This will get the IP address for the DDNS domain and inserts it into a file named "/etc/nginx/conf.d/homeip.inc", then reloads Nginx.

    Step 3
    Make the new file executable

    sudo chmod +x /etc/cron.daily/getddns
    

    Step 4*
    Change the config file to include the new homeip.inc file which contains the allow statement for the DDNS domain. You can see the new line in the "location".

    server {
       listen 80;
       server_name domain.ca;
       return 301 https://$server_name$request_uri;
    }
    
    server {
      listen 443 ssl http2;
      server_name domain.ca;
      
      add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
      add_header X-XSS-Protection "1; mode=block";
      add_header X-Content-Type-Options nosniff;
      add_header Referrer-Policy strict-origin;
      add_header Content-Security-Policy "default-src" always;
      add_header X-Frame-Options SAMEORIGIN;
      ssl_stapling on;
      ssl_stapling_verify on;
      server_tokens off;
    
      ssl on;
      ssl_certificate /etc/letsencrypt/live/domain.ca/fullchain.pem;
      ssl_certificate_key /etc/letsencrypt/live/domain.ca/privkey.pem;
      ssl_session_timeout 5m;
      ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
      ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
      ssl_prefer_server_ciphers on;
      ssl_session_cache shared:SSL:10m;
      ssl_dhparam /etc/ssl/certs/dhparam.pem;
      proxy_cookie_path / "/; secure; HttpOnly";
    
    
        location / {
            include /etc/nginx/conf.d/homeip.inc; #THIS IS THE NEW LINE
        	allow 192.168.1.0/24; #obviously not the real IP range but represents IP range 1
            allow 192.168.2.0/24; #obviously not the real IP range but represents IP range 2
            deny all; #deny all other IPs
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $http_host;
            proxy_set_header X-NginX-Proxy true;
            proxy_pass http://192.168.100.61;
            proxy_redirect off;
    
            # Socket.IO Support
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
       }
    }
    

    That's it. Now the allow/deny rules will be updated once an hour with any changes to the dynamic IP address.

    This is scheduled to run every hour but could be run every day instead if that's too frequent.

    The nice thing about this option rather than using the firewall script from @Romo here is that, users can be presented with an appropriate Access Denied 403 page rather than being blocked at the firewall. For a service like the PiHole, @Romo's script makes more sense but for a website, I like the ability to present the access denied page.

    Edit: Updated for spelling in title



  • Retitle the topic appropriately.



  • This is what I meant when I said look at the other post to get the IP from DynDNS. I did not mean to use the other script. It is a different scenario.



  • @jaredbusch said in Hot To Allow Site Access In Nginx By DDNS Instead Of By IP:

    Retitle the topic appropriately.

    If I thought the topic title wasn't appropriate, I would have titled it differently. I'm not married to the title so if you feel another title would better describe what's here, feel free to make a suggestion.



  • @nashbrydges said in Hot To Allow Site Access In Nginx By DDNS Instead Of By IP:

    #!/bin/bash
    host mydynamicdomain.ddns.net | grep "has address" | sed 's/.*has address //' | awk '{print "allow\t\t" $1 ";\t\t# DDNS IP" }' > /etc/nginx/conf.d/homeip.inc
    service nginx reload > /dev/null 2>&1
    

    This will get the IP address for the DDNS domain and inserts it into a file named "/etc/nginx/conf.d/homeip.inc", then reloads Nginx.

    This will only work for a single dyndns domain. You will have to duplicate this for each user. You have said this was for more than one user.

    It would be better to make the script a bit smarter.

    Have it read the dyndns names from an input file andhave it populate all of them into a single include file.

    1. This means you do not have to edit the crontab to make changes.
    2. This mean you do not have to have multiple includes and multiple crons.


  • @nashbrydges said in Hot To Allow Site Access In Nginx By DDNS Instead Of By IP:

    @jaredbusch said in Hot To Allow Site Access In Nginx By DDNS Instead Of By IP:

    Retitle the topic appropriately.

    If I thought the topic title wasn't appropriate, I would have titled it differently. I'm not married to the title so if you feel another title would better describe what's here, feel free to make a suggestion.

    0_1507225889817_309cbcfc-2fed-46e6-bc1c-b2abc864ea99-image.png



  • @jaredbusch said in Hot To Allow Site Access In Nginx By DDNS Instead Of By IP:

    @nashbrydges said in Hot To Allow Site Access In Nginx By DDNS Instead Of By IP:

    #!/bin/bash
    host mydynamicdomain.ddns.net | grep "has address" | sed 's/.*has address //' | awk '{print "allow\t\t" $1 ";\t\t# DDNS IP" }' > /etc/nginx/conf.d/homeip.inc
    service nginx reload > /dev/null 2>&1
    

    This will get the IP address for the DDNS domain and inserts it into a file named "/etc/nginx/conf.d/homeip.inc", then reloads Nginx.

    This will only work for a single dyndns domain. You will have to duplicate this for each user. You have said this was for more than one user.

    It would be better to make the script a bit smarter.

    Have it read the dyndns names from an input file andhave it populate all of them into a single include file.

    1. This means you do not have to edit the crontab to make changes.
    2. This mean you do not have to have multiple includes and multiple crons.

    That's correct, Nowhere in this thread does it say it works for more than a single user. This however is very useful for me in cases where it will be used for a single user. And this is also a good place for me to start to see if I can adapt it or modify it for more than 1 user. A great learning opportunity for me and others who are still relatively new to using Nginx or Linux.



  • I haven’t tried it but process substitution may work if you enable envsubst.

    So rather than an include you might just be able to do:

    $( nslookup domain.com | yada yada)
    

    Driving so don’t feel like typing out anything.