Skip to content

Instantly share code, notes, and snippets.

@pR0Ps
Last active July 29, 2021 03:07
Show Gist options
  • Save pR0Ps/92217e826bdbf7e5e65db5baf0026c23 to your computer and use it in GitHub Desktop.
Save pR0Ps/92217e826bdbf7e5e65db5baf0026c23 to your computer and use it in GitHub Desktop.
Nginx config for running a public, read-only, multi-user, account-less Plex instance
# Goals:
# - Allow access without having to deal with Plex accounts
# - Only allow read-only access (no changing tags, fixing matches, etc)
# - Don't leak information between users (what has been watched, play progress, etc)
# - Prevent users from claiming the server, changing settings, or acessing internal information
# - Allow administration of the server for people with SSH access to it
# Basic setup instructions:
# - Install Nginx on the same server as Plex and load this config file (usually just a matter of
# dropping it into `/etc/nginx/conf.d`)
# - Configure Plex to accept auth-less access from localhost (through the UI or by setting
# `allowedNetworks` in the server settings)
# - Block access to Plex for anything except localhost using `iptables` (make sure the rules
# persist across reboots!):
# ```
# iptables -A INPUT ! -i lo -p tcp --dport 32400 -j DROP
# ip6tables -A INPUT ! -i lo -p tcp --dport 32400 -j DROP
# ```
# - Visit `http://yourserver` to confirm everything works (also confirm that
# `http://yourserver:32400` _doesn't_ work)
# - Deploy it to your users (add HTTPS, some sort of access control, forward ports, etc)
# Administration
# - Forward local port `32400` to the server using SSH
# (`ssh -L 127.0.0.1:32400:127.0.0.1:32400 yourserver`)
# - Load `http://127.0.0.1:32400` in a browser and administrate away
upstream plex {
server 127.0.0.1:32400;
}
server {
listen 80;
listen [::]:80;
# Make Plex think everything is coming from localhost
# This bypasses IP/domain checks and login redirection
proxy_set_header Host "127.0.0.1";
proxy_set_header Referer "http://127.0.0.1";
proxy_set_header Origin "http://127.0.0.1";
proxy_set_header X-Real-IP "127.0.0.1";
proxy_set_header X-Forwarded-For "127.0.0.1";
# Prevent Plex from responding to requests with compressed data as this would prevent the
# inline modifications to the responses from working.
proxy_set_header Accept-Encoding "";
# Default to only allowing GET requests to Plex.
# (prevents the majority of ways of changing the server's state)
location / {
if ($request_method != "GET") {
return 403;
}
# Rewrite the bare domain to the web interface (unless it's an API call)
set $rewrite 1;
if ($arg_X-Plex-Client-Identifier != '') {
set $rewrite 0;
}
if ($http_x_plex_client_identifier != '') {
set $rewrite 0;
}
if ($rewrite = 1){
rewrite ^/$ $scheme://$http_host/web/index.html redirect;
}
# Rewrite the "myPlexSigninState" key of API responses to tell the frontend that the
# server is claimed, preventing the "server is unclaimed and not secure" warning
# from showing up. This key is returned in requests to both `/` and
# `/media/providers`
sub_filter_types application/json text/xml;
sub_filter_last_modified off;
sub_filter_once on;
sub_filter '"myPlexSigninState":"none"' '"myPlexSigninState":"ok"'; # JSON
sub_filter 'myPlexSigninState="none"' 'myPlexSigninState="ok"'; # XML
proxy_pass http://plex;
}
# Modify the JavaScript frontend
location /web/js/ {
sub_filter_types text/javascript;
sub_filter_last_modified off;
sub_filter_once off;
# Rewrite the Plex auth URL to prevent the frontend from redirecting the user to
# app.plex.tv to sign in. Makes all these requests just load the main page instead.
sub_filter 'https://app.plex.tv/auth' '$scheme://$http_host/';
# Cause the request to the privacy API endpoint on plex.tv to fail. This prevents
# the frontend from retriving the domain of the analytics server, stopping analytics
# from being sent to it.
sub_filter '/user/privacy' '/';
# Prevent the frontend from initiating any websocket connections. Websockets aren't
# required for anything and bypass this proxy and it's modifications.
# TODO: This seems fragile and could break in an update - find a better way
sub_filter '!!window.WebSocket' '!!0';
proxy_pass http://plex;
}
# Allow GET/POST/PUT/DELETE requests to the playQueues endpoint.
# This allows users full control over their individual play queues.
location /playQueues {
proxy_pass http://plex;
}
# Allowing PUT requests to the /library/parts endpoint allows enabling
# subtitles. Unfortunately this setting is persisted different sessions
# so one user changing the setting affects others. It is therefore
# disabled by default.
#location /library/parts/ {
# proxy_pass http://plex;
#}
#####
# Disable a bunch of endpoints that use GET requests to modify things or return sensitive
# information
#####
# Disable claiming the server or changing the settings
location /myplex {
return 403;
}
# Disable access to server/library preferences
location ~* ^/(.+/)?prefs {
return 403;
}
# Disable marking media as played
location ~* ^/:/(un)?scrobble {
return 403;
}
# Disable saving of progress in stream
location ~* ^/:/(timeline|progress) {
return 403;
}
# Disable emptying the trash or starting a manual rescan
location ~* ^/library/sections/.+/(refresh|emptyTrash) {
return 403;
}
# Disable access to the file browser
location /services/browse {
return 403;
}
# Disable access to currently-running activities
location /activities {
return 403;
}
# Disable access to account information
location /accounts {
return 403;
}
# Disable access to device information
location /devices {
return 403;
}
# Disable access to client information
location /clients {
return 403;
}
# Disable access to server status
location /status {
return 403;
}
# Disable access to update information
location /updater {
return 403;
}
# Disable generating access tokens for the server
location /security {
return 403;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment