Controlling NGINX Access by Source IP Behind AWS ALB

Last Updated:

Managing access to your applications when using NGINX behind an AWS Application Load Balancer (ALB) or a proxy can be a bit tricky, especially when you need to control who can or cannot access your web pages.

Typically, NGINX sees only the ALB’s IP address, not the visitor’s. But don’t worry! This easy guide will show you how to pass the real visitor’s IP to NGINX so you can control access effectively.

I’ve set up an ALB that forwards traffic to an NGINX server on AWS. Included in this setup is an “admin” folder for managing the website, which, until now, is accessible from anywhere on the internet. We’ll focus on restricting this access to specific IP addresses.

As mentioned, I have added an “admin” folder that will keep our admin portal code or pages. We will use this admin page and only allow connections to this path from selected IPs or IP ranges.

At the moment, if I go to "http://my-alb-xxx-my-region.elb.amazonaws.com/admin/admin.html", it’s accessible from the internet.

The goal is to restrict access to this admin folder to only my IP address. Let’s see how we can do that.

Understanding Required NGINX Directives

In your NGINX configuration file (typically at /etc/nginx.conf), you’ll use two main directives: set_real_ip_from and real_ip_header. These work together to identify the actual IP addresses of your visitors, even when traffic comes through the ALB.

When a client makes a request through such a proxy or load balancer, the originating IP address of the client is often replaced with the IP address of the proxy/load balancer. To get the real client’s IP address, these Nginx directives are used.

The set_real_ip_from Directive

This directive is used to specify trusted addresses that are known to send the correct client IP address.

Any IP address in the specified range can alter the client IP address that Nginx sees.

It’s important to accurately set this to the range of your reverse proxy or load balancer to prevent IP spoofing where someone could potentially fake their IP address.

For security, set this to your ALB’s IP range to prevent someone from faking their IP address. In our example, we set it to 172.16.0.0/12, a common AWS VPC range.

The real_ip_header Directive

This directive tells Nginx which header to use to retrieve the real client’s IP address.

When a request goes through a proxy or load balancer, these options usually add extra information to the request. This includes the original IP address of the client, which they put in a standard section called ‘X-Forwarded-For‘.

By setting real_ip_header and X-Forwarded-For, you instruct Nginx to use the IP address in the X-Forwarded-For header as the client’s real IP.

This is crucial in environments where the client connects through a proxy or load balancer, as it ensures that Nginx logs, access controls, rate limits, etc., use the actual client IP instead of the IP of the proxy/load balancer.

Configuring NGINX to Recognize Real IP Addresses

This directive is set at the HTTP or HTTPS block level which is above the server block.

...
http {
    set_real_ip_from 172.16.0.0/12; # Replace with your own ALB IP range
    real_ip_header X-Forwarded-For;

    server {
        # Your server configuration
    }
}

How To Block Internet Traffic to /admin Path

To do this, we need to understand the location blocks in NGINX.

In Nginx, a location block is used to define how to process specific types of requests based on the request URI (Uniform Resource Identifier).

It’s a powerful feature that allows for fine-grained control over the configuration depending on the requested URL.

This is particularly useful for applying special configuration rules to specific directories or files on your server.

The location blocks are commonly used for:

  • Defining how to serve static content (like images, CSS, JavaScript).
  • Setting up reverse proxy configurations for different paths.
  • Applying specific security or access rules (like password protection or IP restrictions).
  • Rewriting or redirecting URLs.
  • Handling error pages or specific types of requests.

Coming back to our example, we want to block internet access to the /admin path and its sub-pages on our application.

To do this, we can specify a location directive in our HTTP server configuration

location  /admin {
    allow 2.106.59.29; # Replace with your IP
    deny all;
}

This location blocks controls access to the /admin path of our server. In my case, I’m blocking all the traffic except if it’s originating from my VPN IP address (source IP).

In Nginx configuration, the location block is typically nested within a server block. The server block itself is nested within the HTTP block. You can’t directly nest a location block within the HTTP block; it must be within a server block.

...
http {
    # Global settings that apply to all server blocks
    include       mime.types;
    default_type  application/octet-stream;
    sendfile      on;

    # Here is the directives we added
    set_real_ip_from 172.16.0.0/12;
    real_ip_header X-Forwarded-For;


    # Server block
    server {
        listen       80; # Listen on port 80 for HTTP
        server_name  example.com www.example.com; # Server domain names

        # Location block for root URL
        location / {
            root   /var/www/example.com/html; # Document root
            index  index.html index.htm;      # Default index files
        }

        # Location block for admin URL
        location  /admin {
            allow 2.106.59.29; # Replace with your IP
            deny all; # Everything else will be blocked
        }

    }
}
...

Now that we have these settings in place, we should check the syntax of the NGINX configuration before we reload the service:

sudo nginx -t

You should be seeing a successful message. If this fails, revisit and review your configuration file and check again.

Then reload the NGINX server:

sudo systemctl reload nginx

Make sure NGINX is running:

sudo systemctl status nginx

The NGINX service should be active and running

Verify Your Configuration Changes

The IP address that I whitelisted in that configuration is my VPN address. In order to test it, we visit our /admin/admin.html page as we did before and make sure it works

I can still access my /admin path because I’m connected to the VPN and my IP is the whitelisted IP.

To check your public IP, you can run “curl ifconfig.me” command from your laptop or just Google it.

To make sure our changes worked, I’ll disconnect from the VPN and check the webpage again. This time, I should get “403 Forbidden” error:

We have successfully blocked internet access to our /admin path of our application.

Security Considerations

When setting up these configurations:

  • Ensure proper configuration to avoid exposing sensitive areas.
  • Verify access control is working as intended.
  • Be cautious with X-Forwarded-For to avoid IP spoofing.
  • Use correct logging and rate limiting based on real IP addresses.
  • If using SSL/TLS offloading, ensure secure communication between ALB and NGINX.

Conclusion

In conclusion, we’ve walked through the steps to effectively restrict access to an NGINX server behind an AWS Application Load Balancer (ALB) based on source IP addresses. You’ve learned how to leverage the power of NGINX in conjunction with AWS ALB to ensure that only authorized users can access sensitive areas of your site, such as administrative panels.

RECENT POSTS

Get Ops Pro Tips in Your Inbox!