Skip to content

Commit

Permalink
Merge pull request #47 from westsurname/plex-request-ssl
Browse files Browse the repository at this point in the history
Force ssl
  • Loading branch information
westsurname authored Jan 2, 2025
2 parents 7f5ab8b + 9b2860c commit 8c92686
Show file tree
Hide file tree
Showing 9 changed files with 380 additions and 228 deletions.
9 changes: 2 additions & 7 deletions .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ PLEX_SERVER_HOST=<plex_server_host>
PLEX_SERVER_MACHINE_ID=<plex_server_machine_id>
PLEX_SERVER_API_KEY=<plex_server_api_key>
PLEX_SERVER_MOVIE_LIBRARY_ID=<plex_server_movie_library_id>
PLEX_SERVER_TV_SHOW_LIBRARY_ID=<plex_server_movie_library_id>
PLEX_SERVER_TV_SHOW_LIBRARY_ID=<plex_server_tv_show_library_id>
PLEX_SERVER_PATH=<plex_server_path>

#-------------------------------------------------------------------------#
# OVERSEERR - WATCHLIST, PLEX AUTHENTICATION, PLEX REQUEST, RECLAIM SPACE #
Expand Down Expand Up @@ -123,12 +124,6 @@ BLACKHOLE_RD_MOUNT_REFRESH_SECONDS=200
BLACKHOLE_WAIT_FOR_TORRENT_TIMEOUT=60
BLACKHOLE_HISTORY_PAGE_SIZE=500

#------------------------------#
# PLEX REQUEST - PLEX REQUEST #
#------------------------------#

PLEX_REQUEST_SSL_PATH=

#-----------------------------------------------------------------------------------------------#
# DISCORD - BLACKHOLE, WATCHLIST, PLEX AUTHENTICATION, PLEX REQUEST, MONITOR RAM, RECLAIM SPACE #
#-----------------------------------------------------------------------------------------------#
Expand Down
21 changes: 15 additions & 6 deletions Dockerfile.plex_request_nginx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,23 @@ FROM nginx:alpine
LABEL org.opencontainers.image.source="https://github.com/westsurname/scripts"
LABEL org.opencontainers.image.description="Docker image for the plex_request_nginx service"

# Install required packages
RUN apk add --no-cache \
openssl \
inotify-tools

# Create SSL directory
RUN mkdir -p /ssl && \
chown nginx:nginx /ssl && \
chmod 700 /ssl

COPY plex_request_nginx_variables.conf /etc/nginx/templates/10-variables.conf.template
COPY plex_request_nginx.conf /etc/nginx/conf.d/default.conf
COPY plex_request_nginx_default.conf /etc/nginx/conf.d/default.conf
COPY plex_request_nginx.conf /etc/nginx/nginx.conf
COPY plex_request_nginx_entrypoint.sh /docker-entrypoint.d/40-decrypt-plex-cert.sh

RUN if [ -n "$PLEX_REQUEST_SSL_PATH" ]; then \
sed -i '/# SSL_DISABLED_BEGIN/,/# SSL_DISABLED_END/d' /etc/nginx/conf.d/default.conf; \
else \
sed -i '/# SSL_ENABLED_BEGIN/,/# SSL_ENABLED_END/d' /etc/nginx/conf.d/default.conf; \
fi
# Make the entrypoint script executable
RUN chmod +x /docker-entrypoint.d/40-decrypt-plex-cert.sh

# Expose port 8000 to the outside world
EXPOSE 8000
Expand Down
34 changes: 26 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@
- `PLEX_HOST`: The URL to general Plex services.
- `PLEX_METADATA_HOST`: The URL to the Plex metadata service.
- `PLEX_SERVER_HOST`: The host address of your Plex server.
- `PLEX_SERVER_MACHINE_ID`: The unique machine identifier for your Plex server.
- `PLEX_SERVER_MACHINE_ID`: The processed unique machine identifier for your Plex server.
- `PLEX_SERVER_API_KEY`: Your Plex server's API key for authentication.
- `PLEX_SERVER_MOVIE_LIBRARY_ID`: The library ID for movies on your Plex server.
- `PLEX_SERVER_TV_SHOW_LIBRARY_ID`: The library ID for TV shows on your Plex server.
- `PLEX_SERVER_PATH`: The path to your 'Plex Media Server' folder containing Cache/cert-v2.p12 (e.g., /var/lib/plexmediaserver/Library/Application Support/Plex Media Server).
- **Overseerr** - Watchlist, Plex Authentication, Plex Request, Reclaim Space:
- `OVERSEERR_HOST`: The host address of your Overseeer instance.
Expand Down Expand Up @@ -85,13 +86,6 @@
- `BLACKHOLE_WAIT_FOR_TORRENT_TIMEOUT`: The timeout in seconds to wait for a torrent to be successful before failing.
- `BLACKHOLE_HISTORY_PAGE_SIZE`: The number of history items to pull at once when attempting to mark a download as failed.
- **Plex Request** - Plex Request:
- `PLEX_REQUEST_SSL_PATH` (Optional): The path to SSL certificates for Plex Request. If provided, this directory should contain the following files:
- `fullchain.pem`: The full certificate chain file.
- `privkey.pem`: The private key file.
- `chain.pem`: The certificate chain file.
- `dhparam.pem`: The Diffie-Hellman parameters file.
- **Discord** - Blackhole, Watchlist, Plex Authentication, Plex Request, Monitor Ram, Reclaim Space:
- `DISCORD_ENABLED`: Set to `true` to enable Discord error notifications.
- `DISCORD_UPDATE_ENABLED`: Set to `true` to enable update notifications as well on Discord.
Expand Down Expand Up @@ -132,6 +126,30 @@
python3 python_watcher.py
```
## Plex Request
### Setup
1. Ensure your Plex Media Server is properly configured and running.
2. In your `.env` file, make sure the following variables are set:
- `PLEX_SERVER_PATH`: Points to your 'Plex Media Server' directory containing the certificate
- `PLEX_SERVER_MACHINE_ID`: Your Plex server's machine identifier
- Other required variables as listed in the configuration section

3. Start the Plex Request services:
```bash
docker-compose --profile plex_request up -d
```

### SSL Configuration
The service automatically handles SSL by:
- Using your Plex Media Server's certificate (cert-v2.p12) from the Cache directory
- Converting it to the required format for nginx
- Automatically detecting and handling certificate updates
No manual SSL configuration is needed as long as your Plex Media Server is properly configured.
## Repair
### Usage
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ services:
image: ghcr.io/westsurname/scripts/plex_request_nginx:latest
pull_policy: always
volumes:
- ${PLEX_REQUEST_SSL_PATH:-/dev/null}:${PLEX_REQUEST_SSL_PATH:-/dev/null}:ro
- ${PLEX_SERVER_PATH}:/plex:ro
- ./sockets:/app/sockets
ports:
- 8012:8000
Expand Down
253 changes: 52 additions & 201 deletions plex_request_nginx.conf
Original file line number Diff line number Diff line change
@@ -1,201 +1,52 @@
# https://github.com/toomuchio/plex-nginx-reverseproxy/blob/master/nginx.conf

ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

server {
send_timeout 100m; #Some players don't reopen a socket and playback stops totally instead of resuming after an extended pause (e.g. Chrome)

#Faster resolving, improves stapling time. Timeout and nameservers may need to be adjusted for your location Google's have been used here.
resolver 8.8.4.4 8.8.8.8 valid=300s;
resolver_timeout 10s;

# SSL_DISABLED_BEGIN
listen 8000;
# SSL_DISABLED_END

# SSL_ENABLED_BEGIN
listen 8000 ssl http2;
# ssl
# ssl on; on;
ssl_certificate ${plex_request_ssl_path}/fullchain.pem;
ssl_certificate_key ${plex_request_ssl_path}/privkey.pem;
# ssl_certificate /etc/nginx/cert.pem;
# ssl_certificate_key /etc/nginx/key.pem;

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
#Intentionally not hardened for security for player support and encryption video streams has a lot of overhead with something like AES-256-GCM-SHA384.
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';

#Why this is important: https://blog.cloudflare.com/ocsp-stapling-how-cloudflare-just-made-ssl-30/
ssl_stapling on;
ssl_stapling_verify on;
#For letsencrypt.org you can get your chain like this: https://esham.io/2016/01/ocsp-stapling
# ssl_trusted_certificate ${plex_request_ssl_path}/chain.pem;

#Reuse ssl sessions, avoids unnecessary handshakes
#Turning this on will increase performance, but at the cost of security. Read below before making a choice.
#https://github.com/mozilla/server-side-tls/issues/135
#https://wiki.mozilla.org/Security/Server_Side_TLS#TLS_tickets_.28RFC_5077.29
#ssl_session_tickets on;
ssl_session_tickets off;

#Use: openssl dhparam -out dhparam.pem 2048 - 4096 is better but for overhead reasons 2048 is enough for Plex.
# ssl_dhparam ${plex_request_ssl_path}/dhparam.pem;
ssl_ecdh_curve secp384r1;
# SSL_ENABLED_END

#Plex has A LOT of javascript, xml and html. This helps a lot, but if it causes playback issues with devices turn it off. (Haven't encountered any yet)
gzip on;
gzip_vary on;
gzip_min_length 1000;
gzip_proxied any;
gzip_types text/plain text/css text/xml application/xml text/javascript application/x-javascript image/svg+xml;
gzip_disable "MSIE [1-6]\.";

#Nginx default client_max_body_size is 1MB, which breaks Camera Upload feature from the phones.
#Increasing the limit fixes the issue. Anyhow, if 4K videos are expected to be uploaded, the size might need to be increased even more
client_max_body_size 100M;

#Forward real ip and host to Plex
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
#When using ngx_http_realip_module change $proxy_add_x_forwarded_for to '$http_x_forwarded_for,$realip_remote_addr'
proxy_set_header Sec-WebSocket-Extensions $http_sec_websocket_extensions;
proxy_set_header Sec-WebSocket-Key $http_sec_websocket_key;
proxy_set_header Sec-WebSocket-Version $http_sec_websocket_version;
# Plex Headers
proxy_set_header X-Plex-Client-Identifier $http_x_plex_client_identifier;
proxy_set_header X-Plex-Container-Size $http_x_plex_container_size;
proxy_set_header X-Plex-Container-Start $http_x_plex_container_start;
proxy_set_header X-Plex-Device $http_x_plex_device;
proxy_set_header X-Plex-Device-Name $http_x_plex_device_name;
proxy_set_header X-Plex-Platform $http_x_plex_platform;
proxy_set_header X-Plex-Platform-Version $http_x_plex_platform_version;
proxy_set_header X-Plex-Product $http_x_plex_product;
proxy_set_header X-Plex-Token $http_x_plex_token;
proxy_set_header X-Plex-Version $http_x_plex_version;
proxy_set_header X-Plex-Nocache $http_x_plex_nocache;
proxy_set_header X-Plex-Provides $http_x_plex_provides;
proxy_set_header X-Plex-Device-Vendor $http_x_plex_device_vendor;
proxy_set_header X-Plex-Model $http_x_plex_model;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $http_x_forwarded_for;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;

#Websockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";

#Buffering off send to the client as soon as the data is received from Plex.
proxy_redirect off;
proxy_buffering off;

location / {
proxy_pass ${plex_server_host};
proxy_read_timeout 86400;

proxy_hide_header 'Access-Control-Allow-Origin';
proxy_hide_header 'Access-Control-Allow-Methods';
proxy_hide_header 'Access-Control-Allow-Headers';

add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE, PATCH';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';

if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE, PATCH';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}

# return 302 https://<PLEX_PUBLIC_HOST>$request_uri;

# access_log logs/plex.access.log;

# # enable the next two lines for http auth
# auth_basic "Restricted";
# auth_basic_user_file /config/nginx/.htpasswd;

# enable the next two lines for ldap auth
# auth_request /auth;
# error_page 401 =200 /ldaplogin;
}

location /auth {
proxy_pass http://unix:/app/sockets/plex_authentication.sock;
}

location /web/index.html {
proxy_pass ${plex_server_host}/web/index.html;

# access_log logs/plex.access.log;

sub_filter '</head>' '<script>
function checkToken() {
var token = localStorage.getItem("myPlexAccessToken");
if (token) {
sendTokenToServer(token);
return true;
}
return false;
}

function sendTokenToServer(token) {
fetch("/auth/token", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({token: token}),
})
.then(response => {
if (response.status === 201) {
localStorage.setItem("tokenFound", "true");
}
});
}

function hasFoundBefore() {
return localStorage.getItem("tokenFound") === "true";
}

if (!hasFoundBefore()) {
if (!checkToken()) {
var attempts = 0;
var interval = setInterval(function() {
if (checkToken() || ++attempts >= 60) {
clearInterval(interval);
}
}, 1000);
}
}
</script></head>';
sub_filter_once on;
}

location = /library/all {
proxy_pass http://unix:/app/sockets/plex_request.sock;

# access_log logs/plex.access.log;
}

location ~ ^/library/metadata/[^/]+/children$ {
proxy_pass http://unix:/app/sockets/plex_request.sock;

# access_log logs/plex.access.log;
}

location /library/request/ {
proxy_pass http://unix:/app/sockets/plex_request.sock;

# access_log logs/plex.access.log;
}
}
user nginx;
worker_processes auto;

error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;


events {
worker_connections 1024;
}

stream {
upstream http {
server localhost:8001;
}

upstream https {
server localhost:8002;
}

map $ssl_preread_protocol $upstream {
default https;
"" http;
}

server {
listen 8000;
listen [::]:8000;
proxy_pass $upstream;
ssl_preread on;
}
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;

sendfile on;
#tcp_nopush on;

keepalive_timeout 65;

#gzip on;

include /etc/nginx/conf.d/*.conf;
}
Loading

0 comments on commit 8c92686

Please sign in to comment.