[PHP] PlexAuth - Authentication for your domain using Plex

Have you got ombi running with SSL?

SSL is enabled in Ombi, yes. Am i correct in assuming nginx provides all the certs I need or is there a separate process I need to follow for each application?

Edit: to clarify, I have not used Ombi with SSL before this point since I was expecting this configuration to take care of that.

You need to configure it for each server block in nginx. For testing,
disable SSL within ombi and let nginx handle it.

You should turn off SSL on the Ombi container. The letsencrypt container will handle the SSL.
You should also make sure you are pointing nginx reverse proxy to the Ombi IP and port not your site URL.

Bingo. So I discovered the biggest problem I had was the base URL was not set within Ombi itself and the /requests directory I had set in nginx was reserved by Ombi for one of its pages. I changed both to something different like /requestcontent and removed the https under the siteconfig and it started working. Now to venture into the rest of my applications. Thank you all for your help, this is all very exciting. Someone should get the guys over at HTPCGuides to do a writeup on it so more people get behind it. :slight_smile:

I’m starting to feel like fate doesn’t want me to have this… This morning I disabled setting iptables in docker so that none of my containers leaked without going through nginx. Now when I navigate through the PlexAuth portal, it tells me my username and password are invalid no matter what or whose account I use, I can’t get through. I went as far as to blow away the plexauth directory and start from scratch, but no dice. nginx does give me the following log entry:

2017/02/20 10:27:06 [info] 320#0: *9 client 172.17.0.1 closed keepalive connection 2017/02/20 10:27:06 [info] 320#0: *10 client 172.17.0.1 closed keepalive connection 2017/02/20 10:27:15 [error] 320#0: *11 FastCGI sent in stderr: "PHP message: PHP Warning: array_key_exists() expects parameter 2 to be array, null given in /config/www/plexauth/inc/PlexUser.class.php on line 69" while reading response header from upstream, client: 172.17.0.1, server: my.site, request: "POST /plexauth/ HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "my.site", referrer: "https://my.site/plexauth/" 2017/02/20 10:27:17 [info] 320#0: *11 client 172.17.0.1 closed keepalive connection 2017/02/20 10:27:22 [info] 321#0: *14 client closed connection while waiting for request, client: 172.17.0.1, server: 0.0.0.0:443 2017/02/20 10:27:22 [info] 321#0: *15 client closed connection while waiting for request, client: 172.17.0.1, server: 0.0.0.0:443

Edit: After trying to rebuild my nginx docker I realized my containers didn’t have any Internet access despite being accessible. I re-enabled iptable modification from docker and it started working again. Does anyone have any recommendations on how to lock this down? I’m going to try redeploying some containers without their port variables to avoid docker adding them to the iptable and routing internally.

Edit 2: Yes, not publishing ports and just allowing nginx to route along its own network is the way to go. Also, for those coming across this later: Create a second docker network and set static IPs for your containers with --net NETWORKNAME --ip 172.18.0.##. After rebooting for the first time after getting nginx setup for reverse proxy, all my IPs changed and I had to start over.

Hoping @xxrazorxx can correct me where I could be wrong, but I’m pretty sure you shouldn’t have to go to that extent to get the docker working and be locked down. You should be able to expose port 443 and 80 on your host and configure your docker to respond to those ports. Then configure your nginx docker to reverse proxy to any other desired ports on your host.

I’ve never had to adjust the iptables on a docker before.

It sounds like you ended up getting it going though.

When time permits I plan on spending some time and working on a docker implementation, unfortunately that could be a little ways off though.

I would only worry about your letsencrypt docker. The other containers are behind a your firewall. The only time you should have to worry is if you are an insecure network.

Internet -> Home Router -> Docker (don’t worry about it, the only exposed port should be your letsencrypt container)

Internet -> Shared Router -> Docker (might need more security, just use docker link and remove the port forwarding)

Please be aware of the following commit.

I highly recommend making these changes to your local setups.

Hello hjone72,

First time, wow! Thank you for your project. But I have a question. Can you write a HowTo, for people who are not so good with nginx? I just tested it. But more than the login screen does not work. He fetches the tokens but I can not get to the dashboard.

I would be very glad if you would document the use of PlexAuth something more.

Best regards
Ben

@mUTECX, the error you are seeing is something I am still trying to figure out. I believe it is related to session regeneration that can be found here: PlexAuth/include.php at master · hjone72/PlexAuth · GitHub
If you read back a page or two you’ll find us talk about it. Hopefully I’ll be able to make some time soon and turn it into a docker :slight_smile:

If you need help after trying the regeneration stuff flick me a PM with some log files and i’ll try and help out.

Thanks,

I’ve been banging my head against the wall for hours on this one. I’m running this in a jail in FreeNAS.

I had it working without the auth_request addition to nginx. I then went to add this, and nothing works now. I’m currently getting a " peer closed connection in SSL handshake while SSL handshaking to upstream, client: x.x.x.x, server: x.x.x, request: “GET /favicon.ico HTTP/1.1”, subrequest: “/auth/”" error.

I’m definitely new to web servers – is the auth_request necessary if it worked without it? I was able to authenticate my user with the token and it seemed to work perfectly.

The website may look like it is working, but without auth_request it won’t be preventing guests from accessing your sites.

Did you compile nginx from source to include the auth_request module? If so, did you include SSLhttp_ssl_module?

Doing a nginx -V shows I have both http_auth_request_module and http_ssl_module availabe.

For reference, here are my files. Maybe you can spot a simple type that I can’t. I have used the include parameter to add the files ssl_common.conf and proxy_setup.conf.

This is the full error I get in the log. 10.1.1.1 is my gateway – not sure if that has anything to do with it.

2017/05/23 09:57:15 [error] 56235#102300: *4 peer closed connection in SSL handshake while SSL handshaking to upstream, client: 10.1.1.1, server: login.domain.com, request: "GET /favicon.ico HTTP/1.1", subrequest: "/auth/", upstream: "https://127.0.0.1:8087/auth/", host: "login.domain.com"
2017/05/23 09:57:15 [error] 56235#102300: *4 auth request unexpected status: 502 while sending to client, client: 10.1.1.1, server: login.domain.com, request: "GET /favicon.ico HTTP/1.1", host: "login.domain.com"
2017/05/23 10:09:31 [error] 56235#102300: *7 peer closed connection in SSL handshake while SSL handshaking to upstream, client: 10.1.1.1, server: login.domain.com, request: "GET / HTTP/1.1", subrequest: "/auth/", upstream: "https://127.0.0.1:8087/auth/", host: "login.domain.com", referrer: "https://login.domain.com/"
2017/05/23 10:09:31 [error] 56235#102300: *7 auth request unexpected status: 502 while sending to client, client: 10.1.1.1, server: login.domain.com, request: "GET / HTTP/1.1", host: "login.domain.com"

nginx.conf server blocks

server {
        listen 80;
        server_name login.domain.com;
        return 301 https://login.domain.com$request_uri;
}

server {
        listen 443 ssl http2;
        server_name www.login.domain.com;
        return 301 https://login.domain.com$request_uri;
}

 server {
        listen 443 ssl http2 default_server;
        listen [::]:443 ssl http2 default_server;
        server_name login.domain.com;

        access_log /var/log/nginx/login.domain.com_access.log;
        error_log /var/log/nginx/login.domain.com_error.log info;

        include ssl_common.conf;
        include proxy_setup.conf;

        set $return $request_uri;
        set $return_host $host;
        auth_request /auth/;
        
        error_page 401 = @error401;
        location @error401 {
            add_header 'X-AfterAuth' 'test';
            add_header X-Original-URI $request_uri;
            if ($return != false) {
                rewrite ^ https://login.domain.com?return=$return_host$return redirect;
            }
            return 302 https://login.domain.com;
        }

        error_page 403 = @error403;
        location @error403 {
            return 302 https://login.domain.com/access_denied.html;
        }

        location ~ \.php$ {
                try_files $uri =404;
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                fastcgi_pass 127.0.0.1:9000;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include /usr/local/etc/nginx/fastcgi_params;
        }
    }

server {
        listen 8087 ssl http2;
        listen [::]:8087 ssl http2;
        server_name = 127.0.0.1;

        error_log /var/log/nginx/login.domain.com_auth_error.log info;

        root /usr/local/www/login.domain.com/;
        index index.php index.html;

        location ~ \.php$ {
                try_files $uri =404;
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                fastcgi_pass 127.0.0.1:9000;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include /usr/local/etc/nginx/fastcgi_params;
        }
}

ssl_common.conf (stripped down for testing with some things commented out)

ssl_prefer_server_ciphers on;
ssl_certificate /usr/local/etc/letsencrypt/live/domain.com/fullchain.pem;
ssl_certificate_key /usr/local/etc/letsencrypt/live/domain.com/privkey.pem;

# Disable SSLv2 and SSLv3 (BEAST and POODLE attacks)
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

# Enable our strong DH Key
ssl_dhparam /usr/local/etc/ssl/dhparams.pem;

# Cipher-list for PFS.
ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;

# Requires nginx >= 1.1.0
ssl_session_cache builtin:1000 shared:SSL:10m;

proxy_setup.conf

location /admin_auth/ {
proxy_pass https://127.0.0.1:8087/auth/index.php?admin=true&uri=$return;
proxy_pass_request_body off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Original-URI $request_uri;
proxy_set_header Content-Length '0';
}

location /auth/ {
proxy_pass https://127.0.0.1:8087/auth/;
proxy_pass_request_body off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Original-URI $request_uri;
proxy_set_header Content-Length '0';
}

Lets go back to basics. Can you start by disabling SSL on the localhost:8087 bit? I believe there are extra steps required if you are trying to proxy SSL connections.

Back to square 1 I suppose. I’ve stripped out the SSL and am trying just to get this to work with strictly HTTP. The following code brings up the PHP login page and I can successfully login (and fail a login with made up credentials). No redirect, obviously.

nginx.conf

server {
        listen 80;
        server_name login.domain.com;

        root /usr/local/www/login.domain.com;
        index index.php index.html;

        access_log /var/log/nginx/login.domain.com_access.log;
        error_log /var/log/nginx/login.domain.com_error.log info;

    location ~ \.php$ {
                try_files $uri =404;
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                fastcgi_pass 127.0.0.1:9000;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include fastcgi_params;
        }

If I add in some redirect, this is the code I’m using.

nginx.conf

server {
        listen 80;
        server_name login.domain.com;

        access_log /var/log/nginx/login.domain.com_access.log;
        error_log /var/log/nginx/login.domain.com_error.log info;

        root /usr/local/www/login.domain.com;
        index index.php index.html;

        set $return $request_uri;
        set $return_host $host;

        auth_request /auth/;

        location /auth/ {
                proxy_pass http://localhost:8087/auth/;
                proxy_pass_request_body off;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Original-URI $request_uri;
                proxy_set_header Content-Length '0';
        }

        location /admin_auth/ {
                proxy_pass http://localhost:8087/auth/index.php?admin=true&uri=$return;
                proxy_pass_request_body off;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Original-URI $request_uri;
                proxy_set_header Content-Length '0';
        }

location ~ \.php$ {
                try_files $uri =404;
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                fastcgi_pass 127.0.0.1:9000;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include fastcgi_params;
        }
}

server {
        listen 8087;
        server_name = localhost;

        error_log /var/log/nginx/login.domain.com_auth-error.log info;

        root /usr/local/www/login.domain.com/;
        index index.php index.html;

        location ~ \.php$ {
                try_files $uri =404;
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                fastcgi_pass 127.0.0.1:9000;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include fastcgi_params;
        }
}

Here’s where the issues start. Instead of a php login page, I get a dialog box popping up for authentication. If I then put my credentials in (they must be correct), I get to the php page. Then any login / username works (even test / test).

If I don’t include the following in the server block for the redirect, I get a 404 after the basic authentication box with the following error (I’m not sure where it’s picking up to find the index.html file).

        root /usr/local/www/login.domain.com;
        index index.php index.html;
2017/05/23 16:21:18 [error] 74782#102530: *1 "/usr/local/etc/nginx/html/index.html" is not found (2: No such file or directory) while sending to client, client: x.x.x.x

I’m going down a rabbit hole here and I have no idea what is going on. I’m tempted to scrap this on FreeNAS and move to Ubuntu.

@isentropik jump onto my discord and we’ll chat there. https://discord.gg/nhh5v2

EDIT: Here is a link that doesn’t expire: https://discord.gg/ntsgcQ4

Solved! With a big thanks to @hjone72 .

The issue was this snippet:

auth_request /auth/;

That shouldn’t be on your login page or it’ll ask you to authenticate just to get to the page. Any subsequent page that someone wants user authentication should have that snippet, however.

Regarding my SSL issues, all of the https located in these parts needed to be removed and replaced with http:

proxy_pass https://127.0.0.1:8087/auth/;
...
rewrite ^ https://login.domain.com?return=$return_host$return redirect;
...
return 302 https://login.domain.com/access_denied.html;

…etc. The only place the https should be is in the redirect server block. Also, my main server block was missing this as well:

        root /usr/local/www/login.domain.com;
        index index.php index.html;

…which the example here doesn’t have because the example assumes a protected site already (behind the login).

Thanks again for this great tool. Is Plex and Ombi SSO on the roadmap for 2018? I’ve seen Organizr add it recently and was wondering if it was possible with PlexAuth down the line.

Organizr added support for Plex and Ombi sso?

Time really hasn’t been friendly lately… Hopefully that changes with the new year :slight_smile:

With Causefx doing such a good job with Organizr I thought most people would have migrated to using that.