Unable to use subrequest authentication for proxied site

Lists lists at benjamindsmith.com
Sat Sep 19 16:26:57 UTC 2020


How do I configure nginx to use subrequest authentication for a reverse proxied 
application with websocket upgrades? The documentation doesn't seem to contain 
the information I need to do this. 

https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-subrequest-authentication/

When I do, I either get
   A) 404 for every request. 
   B) All requests pass regardless of php session ID (or not at all) or 

It seems that I can either proxy the authentication request (and nginx 
attempts to serve local content) OR proxy the request itself (and then I get 
the etherpad content without it being authenticated) I can't seem to get both 
working together. 


SYSTEM CONFIGURATION  

I have an instance of Etherpad running on a VM on a host called Alpha. Alpha 
is running CentOS 7, the VM is running Centos8. 

The Etherpad instance is used in a PHP application running on another server, 
and it's important to prevent the etherpad pages from being public, so it 
seems the easiest way to secure things is to use subrequest authentication. 

Applcation: https://mydomain.com 
Etherpad URL: https://etherpad.mydomain.com

1) Within the application, the URL for the etherpad iframe has 
&sess_id=$php_session_id added. EG: 

https://etherpad.mydomain/p/MyPad?sess_id=MyPhpSessId

This is so that nginx can make a subrequest to the application, passing the 
PHP Sessid to validate that the end user with the PHP Sessid has rights to the 
document in the etherpad instance. 

2) The application has an external authentication page at /external/
etherpad.php. It returns http code 200 when the values in the php session 
match the URL of the etherpad address as passed in the header  X-Original-URI. 
(EG: "MyPad" is referenced in MyPhpSessid) and 401 when this test fails. 

3) Nginx is set up on Alpha to proxy to the VM called Virtual. The proxy is 
set up with Let's Encrypt SSL certificates; the etherpad instance is not 
encrypted but has no direct ports to the public cloud. 



WHAT I'M EXPECTING FOR A SUCCESSFUL HIT 

1) nginx receives a request like: 
https://etherpad.mydomain.com/p/MyPadId?sess_id=PhpSessionId

2) nginx makes a request to 
https://mydomain.dom/external/etherpad.php 
with header: 
X-Original-URI: /p/MyPadId?sess_id=PhpSessionId

3) /external/etherpad.php receives authentication request, gets X-Original-URI 
and validates the request, returning an http_code 200 when all is well. 

4) nginx receives the result of #3, and passes the original request to the 
etherpad instance. 

5) Etherpad responds with the result

6) Successful Etherpad instance runs in the application. 



*********************************************************
WHAT IS HAPPENING (Schenario 1) 

1) nginx receives a request like: 
https://etherpad.mydomain.com/p/MyPadId?sess_id=PhpSessionId

2) nginx makes a request to 
https://mydomain.dom/external/etherpad.php 
with header: 
X-Original-URI: /p/MyPadId?sess_id=PhpSessionId

3) /external/etherpad.php receives authentication request, gets X-Original-URI 
and validates the request, returning an http_code 200 when all is well. 

4) Nginx attempts to resolve the request as a local file, can't find it, and 
returns 404 to the end user. 

SCENARIO 1 NGINX CONFIG FILE 

----------------------
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
  server_name etherpad.mydomain.com;
  listen 8008;

    listen 9000 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/etherpad.mydomain.com/fullchain.pem; 
# managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/etherpad.mydomain.com/
privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot


  location / {
    proxy_pass http://192.168.122.61:9001/;
    proxy_set_header  Host $host;
    proxy_pass_header Server;
    proxy_set_header  X-Real-IP  $remote_addr;
    proxy_set_header  X-Forwarded-For $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme; 
    proxy_http_version 1.1;  # recommended with keepalive connections
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
  }
  location /p/ {
       auth_request /auth;
       auth_request_set $auth_status $upstream_status;
       }
  location /auth {
        internal;
        proxy_pass https://mydomain.com/external/etherpad.php;
        proxy_pass_request_body off;
        proxy_set_header Content-Length "";
        proxy_set_header X-Original-URI $request_uri;
        }
}
----------------------

*********************************************************
WHAT IS HAPPENING (Schenario 2: invalid session ID) 

1) nginx receives a request like: 
https://etherpad.mydomain.com/p/MyPadId?sess_id=INVALID

2) nginx makes a request to 
https://mydomain.dom/external/etherpad.php 
with header: 
X-Original-URI: /p/MyPadId?sess_id=INVALID

3) /external/etherpad.php receives authentication request, gets X-Original-URI 
and validates the request, returning an http_code 401.

4) nginx IGNORES the result of #3, and passes the original request to the 
etherpad instance, even when validation fails.  

5) Etherpad responds with the result

6) Successful Etherpad instance runs in the application despite lacking 
credentials.  


SCENARIO 2 NGINX CONFIG FILE 
----------------------
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
  server_name etherpad.mydomain.com;
  listen 8008;

    listen 9000 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/etherpad.mydomain.com/
fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/etherpad.mydomain.com/
privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

  location / {
    proxy_pass http://192.168.122.61:9001/;
    proxy_set_header  Host $host;
    proxy_pass_header Server;
    proxy_set_header  X-Real-IP  $remote_addr;
    proxy_set_header  X-Forwarded-For $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme; 
    proxy_http_version 1.1;  # recommended with keepalive connections
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
  }
location /p/ {
       auth_request /auth;
       auth_request_set $auth_status $upstream_status;

      # NOTE THAT THIS IS A COPY OF LOCATION / UNTIL THE END OF THIS BLOCK. 
    proxy_pass http://192.168.122.61:9001/;
    proxy_set_header  Host $host;
    proxy_pass_header Server;
    proxy_set_header  X-Real-IP  $remote_addr;
    proxy_set_header  X-Forwarded-For $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme; 
    proxy_http_version 1.1;  # recommended with keepalive connections
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
       }
  location /auth {
        internal;
        proxy_pass https://mydomain.com/external/etherpad.php;
        proxy_pass_request_body off;
        proxy_set_header Content-Length "";
        proxy_set_header X-Original-URI $request_uri;
        }

}

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: This is a digitally signed message part.
URL: <http://mailman.nginx.org/pipermail/nginx/attachments/20200919/fcbab907/attachment.bin>


More information about the nginx mailing list