Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Authentication from a *remote* Nginx? #183

Closed
n4kre opened this issue Oct 30, 2017 · 6 comments
Closed

Authentication from a *remote* Nginx? #183

n4kre opened this issue Oct 30, 2017 · 6 comments
Labels
priority/2/high High priority items type/enhancement Similar to a feature but less impactful

Comments

@n4kre
Copy link

n4kre commented Oct 30, 2017

Hi,

I fail to use Authelia in case the Nginx reverse proxy serving “protected” resources is not on the same machine than Authelia, i.e. when the authentication subrequest is not made to http://127.0.0.1:xxx/verify but to https://login.example.com/verify—assuming the latter point to a remote (different machine) Nginx reverse proxy that is configured “like in your examples”—the latter runs on the same machine than Authelia.

Here is the setup:

https://login.example.com (Nginx, machine B) --> http://127.0.0.1:xxx (Authelia, machine B)
                 ^
                 |
     [authentication subrequest]
                 |
                 |
https://private.example.com (Nginx, machine A)

On the Nginx instance serving https://private.example.com (meant to be protected by an Authelia instance running on another machine; with its own Nginx reverse proxy), I have naively tried to use the Nginx configuration you provide in your examples to add Authelia's protection. I simply have replaced proxy_pass http://127.0.0.1:xxx;—in the block pointed by the auth_request directive—with proxy_pass https://login.example.com/verify; proxy_ssl_server_name on;.
Looking at the requests received by Authelia's backend, the X-Original-URI HTTP header is set to /verify, which is not what we want—it is set in this way because of proxy_set_header X-Original-URI $request_uri; in the Nginx configuration serving https://login.example.com —, but Host is not rewritten—still the original (sub)domain, which is good. For X-Forwarded-Proto, it is rewritten in the same way to https—but it does not mean the client has connected over HTTPS: it is the connection that is made between the two Nginx.
Therefore, from Authelia point of view, the client asked for https://private.example.com/verify. That is inconsistent: only private.example.com is actually what has been requested by the client. The scheme and URI are the ones from the proxy_pass I was talking about earlier.
Then, even though Authelia answers with an HTTP status code 401—again, not for good reasons: Authelia did not get the input we want—, the Nginx server that serves https://login.example.com replies with HTTP status code 200 to the original Nginx server—serving https://private.example.com. Therefore, the authentication subrequest succeeds and grant the user the permission to access the resource. Nothing has run the way I wanted…

Note: I have made these observations via a packet capture on Authelia's machine. (tcpdump -i lo -w capture.pcap 'tcp port xxx', after what I open capture.pcap with WireShark and apply a basic http filter.)

My guess is that it is somehow related to Nginx configurations, but I cannot figure out how to change them so that it works. Any idea?

It would be nice to have an API (over HTTPS) to provide authentication through a centralized instance of Authelia. The idea is to still have https://login.example.com to access Authelia's web UI—where the user provides its credentials/TOTP/U2F—, and to have something like https://auth.example.com to be used “under the hood” by any Nginx (remote) server which serves resources meant to be protected by this single, centralized instance of Authelia.

@n4kre n4kre changed the title Support authentication if Authelia is note on the same machine Authentication from a *remote* Nginx? Oct 30, 2017
@clems4ever
Copy link
Member

Hello @n4kre , thank you for the report.

For your information the setup you propose is indeed not tested yet. I have only tested with nginx serving both Authelia and the app to be protected so far. It might be good to replace the original setup with yours since it is more generic. It is simple to reproduce with docker-compose btw.

Thank you,
Clément.

@clems4ever clems4ever added type/enhancement Similar to a feature but less impactful priority/2/high High priority items labels Oct 31, 2017
@masau
Copy link

masau commented Nov 1, 2017

This is similar to my (working) setup.

The only difference is that I've got an added portal server in front of both Authelia and my service

So requests follow this flow:

Request for https://private.example.com
                                   |
                                   ▼
https://private.example.com (nginx on portal)
                                   |
                                   ▼
       https://authelia.example.com/verify [authentication subrequest]
                                   |
                                   ▼
              https://authelia.example.com
                      (nginx on authelia)                             
                                   |
                                   ▼
                     http://127.0.0.1:xxx  
                    (authelia on authelia)   
                                   |
                                   ▼
                         User is authenticated?
                       |                               |
                       no                             yes
                       |                               |
                       ▼                               ▼
Redirect to:                             https://private.example.com
https://authelia.example.com              (nginx on private)
  (nginx on authelia)                             
         |                                                 |
         ▼                                                 ▼
http://127.0.0.1:xxx                        http://127.0.0.1:xxx
  (authelia on authelia)                        (service on private)

The only difference between your setup and mine (if I'm understanding your explanation correctly) is that on successful authentication your machine A handles requests directly rather then forwarding them to another machine.

Here is my current nginx configuration for a 2fa protected service:
Portal:

server {
  listen 443;
  server_name private.example.com;

  resolver 192.168.1.1; #required to resolve backend from domain name properly in the event of IP changes
  set $backend "https://private.example.com";

  ssl on;
  ssl_certificate /etc/nginx/certs/private.example.com/fullchain.pem;
  ssl_certificate_key /etc/nginx/certs/private.example.com/privkey.pem;
  ssl_session_cache shared:SSL:10m;
  client_max_body_size 1024m;

  location /auth_verify {
    internal;
    proxy_pass_request_body off;
    proxy_set_header  X-Original-URI $request_uri;
    proxy_set_header  X-Real-IP $remote_addr;
    proxy_set_header  Host $http_host;
    proxy_set_header  Content-Length "";

    proxy_pass        https://authelia.example.com/verify;
  }

  location / {
    auth_request /auth_verify;

    auth_request_set $redirect $upstream_http_redirect;
    proxy_set_header Redirect $redirect;

    auth_request_set $user $upstream_http_remote_user;
    proxy_set_header X-Forwarded-User $user;

    auth_request_set $groups $upstream_http_remote_groups;
    proxy_set_header Remote-Groups $groups;

    error_page 401 =302 https://authelia.example.com?redirect=$redirect;
    error_page 403 = https://authelia.example.com/error/403;

    proxy_pass $backend;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP  $remote_addr;
    proxy_set_header X-Forwarded-For $remote_addr;

    # re-write redirects to http as to https
    proxy_redirect http:// https://;
  }
  error_log /var/log/nginx/private_error.log;
  access_log /var/log/nginx/private_access.log;
}

Authelia:

server {
  listen 443;
  server_name authelia.example.com;

  set $backend "http://127.0.0.1:xxx";

  ssl on;
  ssl_certificate /etc/nginx/certs/authelia.example.com/fullchain.pem;
  ssl_certificate_key /etc/nginx/certs/authelia.example.com/privkey.pem;
  ssl_session_cache shared:SSL:10m;
  client_max_body_size 1024m;
  location / {
    proxy_pass $backend;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header X-Real-IP  $remote_addr;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-Forwarded-Ssl on;

    # re-write redirects to http as to https, example: /home
    proxy_redirect http:// https://;
  }

  error_log /var/log/nginx/authelia_error.log;
  access_log /var/log/nginx/authelia_access.log;
}
server {
  listen 80 default_server;
  server_name _;
  rewrite ^ https://$host$request_uri? permanent;
}

@clems4ever
Copy link
Member

Hello @masau, very interesting setup! Amazing!

@masau , @n4kre , you have both made an amazing work so far! I will definitely reuse your documentation to bootstrap a concrete complex highly available setup with terraform and ansible.
Do not hesitate to contribute if you have already scripted something.

@n4kre
Copy link
Author

n4kre commented Nov 2, 2017

Got it!
It was due to the proxy_intercept_errors on; directive in Authelia's Nginx config. A different Nginx location block for /api/verify is required, without this directive.

See #178 for a complete setup, from A to Z, and with Fail2Ban. Lots of comments are provided. :)

@clems4ever
Copy link
Member

Awesome! I will add the tutorial to the wiki I've just created when I have some time.
Thank you!

@clems4ever
Copy link
Member

I added the tutorial to the wiki: https://github.com/clems4ever/authelia/wiki. I can now close the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
priority/2/high High priority items type/enhancement Similar to a feature but less impactful
Projects
None yet
Development

No branches or pull requests

3 participants