Combine Apache’s HTTP authentication with X-Forwarded-For IP whitelisting in Varnish

Want to help support this blog? Try out Oh Dear, the best all-in-one monitoring tool for your entire website, co-founded by me (the guy that wrote this blogpost). Start with a 10-day trial, no strings attached.

We offer uptime monitoring, SSL checks, broken links checking, performance & cronjob monitoring, branded status pages & so much more. Try us out today!

Profile image of Mattias Geniar

Mattias Geniar, December 14, 2014

Follow me on Twitter as @mattiasgeniar

Such a long title for a post. If you want to protect a page or an entire website with HTTP authentication, but also want to whitelist a few fixed IPs (for instance: office or VPN IPs), you can combine both authentication mechanismes in Apache via .htaccess files.

The full example goes like this.

AuthName "User + Pass required"
AuthUserFile /path/to/your/htpasswd
AuthType Basic
Require valid-user
Order Deny,Allow
Deny from all

# Normal whitelist would just add Allow directives
Allow from 12.34.12.34
Allow from 12.34.12.35

# But if your site is behind Varnish, all connections will appear
# to come from the Varnish IP, most likely 127.0.0.1
# or the IP from the host itself.
# 
# So we set the X-Forwarded-For header in Varnish, and filter 
# on that header in Apache's htaccess / Directory access control.
#
# Allow from an IP in the X-Forwarded-For header (Varnish?)
SetEnvIf X-Forwarded-For ^12\.34\.12\.34 env_allow_1
Allow from env=env_allow_1

# Allow from another IP in the X-Forwarded-For header
SetEnvIf X-Forwarded-For ^12\.34\.12\.35 env_allow_2
Allow from env=env_allow_2

# Either the HTTP authentication needs to be correct, or the custom
# environment that allowed the X-Forwarded-For header
Satisfy Any

And when we break this time even more, it’ll show 2 clear methods.

HTTP authentication in .htaccess

The first part is the HTTP authentication, usernames and passwords, for Apache. You can set this in your Apache vhost config, or in an .htaccess file in the directory you want to secure.

AuthName "User + Pass required"
AuthUserFile /path/to/your/htpasswd
AuthType Basic
Require valid-user
Order Deny,Allow
Deny from all

This sets the AuthUserFile to the path where the usernames/passwords are stored. If there’s no such file yet, you can create one with the htpasswd tool at the CLI.

$ htpasswd -c /path/to/your/htpasswd $username

Replace $username with what you want as a username. You’ll be prompted for a password for that user. If you want to append a username/password to an existing file, or want to modify the password of a user in an existing file, use the same CLI command without the -c parameter.

$ htpasswd /path/to/your/htpasswd $username

And you’re set: your HTTP authentication password file exists.

IP Whitelisting with X-Forwarded-For

Normal IP whitelisting for this scenario would be done like this.

Allow from 10.0.1.1
Allow from 10.0.1.2
...

But this uses the IP of the client connecting to Apache, which in the case of a reverse proxy config (like Nginx or Varnish), would nearly always be 127.0.0.1 or the IP of the connecting proxy. Not the client’s IP (unless you would be running a transparant proxy). We can retrieve that via the X-Forwarded-For, if it’s set in the Varnish configs.

To check if your Varnish config has this enabled, search for the line that says something like this.

...
set req.http.X-Forwarded-For = client.ip;
...

It can exist in many variants, but the set req.http.X-Forwarded-For is always the same. If it’s missing, add it at the top of the vcl_recv routine. If that doesn’t work, have a look at my Varnish configs for some ideas.

Now, the Apache bit. You can perform a check on HTTP headers and allow or deny authentication based on that.

SetEnvIf X-Forwarded-For ^12\.34\.12\.35 env_allow_1
Allow from env=env_allow_1
Satisfy Any

The above example will set an environment variable called env_allow_1, if the X-Forwarded-For HTTP header matches the regex ^12\.34\.12\.35, which in human words means as much as “the X-Forwarded-For header must start with 12.23.12.35". If that’s the case, the environment variable env_allow_1 will be set.

The Allow from code allows you to check for environment variables, to allow or deny access.



Want to subscribe to the cron.weekly newsletter?

I write a weekly-ish newsletter on Linux, open source & webdevelopment called cron.weekly.

It features the latest news, guides & tutorials and new open source projects. You can sign up via email below.

No spam. Just some good, practical Linux & open source content.