Nginx – Inconsistent behavior with Nginx’s auth_request_set and more_set_input_headers

debianhttp-headersnginxPHPsession

I'm trying to use the auth_request module in conjunction with the more_set_input_headers to automatically login my users into web apps.

Basically, it works like this:

  1. Users have some session cookie that authenticates them.
  2. I have a PHP script (auth.php) that validates the cookie and returns their proper username as a response header.
  3. Nginx calls auth.php with auth_request, and sets the username in a variable.
  4. Nginx then calls the web app with a request header set to the correct username.
  5. The web app reads the header, and logs the user in.

This works, but strangely inconsistent. The issue is that when a user accesses the web app on /app/, it works, but when the app is accesssed on /app/index.php, it never receives the header from nginx.

I've created a mock configuration that reproduces the error.

Nginx site config:

server {
    server_name www.example.com
    index index.php index.html;

    # --- Internal auth
    location /auth {
        internal;
        root /var/www/sf;

        location /auth/auth.php {
            fastcgi_pass unix:/var/run/php5-fpm.sock;
            include includes/fastcgi_params;

            fastcgi_pass_request_body off;
            fastcgi_param CONTENT_LENGTH 0;
        }

        location /auth {
            deny all;
        }
    }

    location / {

        auth_request /auth/auth.php;

        auth_request_set $auth_header $upstream_http_x_auth_header;
        more_set_input_headers 'X-Test-Header: $auth_header';

        location /app {
            root /var/www/sf;

            # Allow these locations
            location = /app/ {
                allow all;
            }

            location /app/index.php {
                fastcgi_pass unix:/var/run/php5-fpm.sock;
                include includes/fastcgi_params;
            }

            # Deny everything else
            location /app/ {
                deny all;
            }
        }

    }
}

/var/www/sf/auth/auth.php:

<?php

// Mock precondition checker
function is_allowed() {
    return true;
}

// Set the default response code
http_response_code(403);

if(!is_allowed())
    exit();

// Return our header and the OK response code
header("X-Auth-Header: myusername");
http_response_code(200);

/var/www/sf/app/index.php:

<?php

if(empty($_SERVER["HTTP_X_TEST_HEADER"]))
    exit("No header was supplied by nginx.");

exit("Nginx supplied header value: ". $_SERVER["HTTP_X_TEST_HEADER"]);

Response when doing a GET request to /app/:

Nginx supplied header value: myusername

Response when doing a GET request to /app/index.php:

No header was supplied by nginx.

Would anyone have an idea as to what is going on here?

I'm using Debian Wheezy with the nginx-extras package from DotDeb (1.6.0-1~dotdeb.1).

(a small note: when you replace 'more_set_input_headers 'X-Test-Header: $auth_header';' with 'more_set_input_headers 'X-Test-Header: foobar';', the web app always receives a header)

Best Answer

This behaviour is likely due to the fact that more_set_input_headers handler is executed before access phase (where auth_request works), and hence only changes a request if it's internally redirected.

Solution to the problem is to stop using more_set_input_headers (it's anyway very wrong, request headers shouldn't be changed) and use native fastcgi_param instead:

fastcgi_param HTTP_X_TEST_HEADER $auth_header;

Copied from http://mailman.nginx.org/pipermail/nginx/2014-June/044281.html.