Varnish - SSL Redirection and Basic Authentication

Varnish has proven itself to be quite brilliant and blindingly fast when it comes to caching. Quite recently I was in a situation where varnish was used as a frontend with Nginx serving out a web application and node serving out API services. The requirements for varnish were to honour cache headers churned out by the application, redirect all incoming requests to https and have basic authentication in place.

Now varnish doesn’t redirect SSL out of the box nor does it have a straight forward way of putting basic authentication in place. In order to handle SSL redirection, this is the most promising solution I’ve found. The solution involves using the vcl_error module to detect a request via the vcl_recv module and perform a redirection with the proper HTTP code.

Address SSL Redirection

In the vcl_recv segment, right at the beginning put the following condition

sub vcl_recv {
    # SSL redirection
    if ( req.http.host ~ "^(?i)(www\.)?.*(\.<domain>)" && req.http.X-Forwarded-Proto !~ "(?i)https" ) {
        set req.http.x-Redir-Url = "https://" + req.http.host + req.url;
        error 750 req.http.x-Redir-Url;
    }
    ...
    return (lookup);
}

In the vcl_error segment, define the error and operation

sub vcl_error {
    if (obj.status == 750) {
        set obj.http.Location = obj.response;
        set obj.status = 302;
    }
    ...
    return (deliver);
}

This means any request coming for http://www. or http:// would be captured under error code 750 and as a part of error handling redirected to the SSL listener with HTTP status 302. Much detailed examples of redirection can be found at Redirect In VCL

Address Basic Authentication

Scenario 1: All incoming requests should pass through Basic Authentication.

Generate Base64 Basic Authentication via command line.

$ echo -n "user:password" | base64

In the vcl_recv, after the SSL redirection include the following logic

sub vcl_recv {
    if ( req.http.Authorization !~ "Basic dXNlcjpwYXNzd29yZA==" #user:password
    ) {
        error 401 "Restricted";
    }
    ...
    return (lookup);
}

Scenario 2: All incoming requests except a defined ACL should pass through Basic Authentication.

At the beginning of the vcl define an ACL with the host IPs allowed without basic authentication.

acl localhost {
    "localhost";
    "127.0.0.1";
}

Modify the vcl_recv as below

sub vcl_recv {
    if ( client.ip !~ localhost && req.http.Authorization !~ "Basic dXNlcjpwYXNzd29yZA==" #user:password
    ) {
        error 401 "Restricted";
    }
    ...
    return (lookup);
}

Scenario 3: All incoming requests except for a few specific requests should pass through Basic Authentication.

Modify the vcl_recv as below

sub vcl_recv {
    if ( req.url !~ "/version" && req.http.Authorization !~ "Basic dXNlcjpwYXNzd29yZA==" #user:password
    ) {
        error 401 "Restricted";
    }
    ...
    return (lookup);
}