Skip to content

Instantly share code, notes, and snippets.

@MarMed
Last active February 29, 2024 19:23
Show Gist options
  • Star 92 You must be signed in to star a gist
  • Fork 21 You must be signed in to fork a gist
  • Save MarMed/94b5537a9fb61cf7212808692bbef14d to your computer and use it in GitHub Desktop.
Save MarMed/94b5537a9fb61cf7212808692bbef14d to your computer and use it in GitHub Desktop.
Routing plex traffic through an SSH tunnel

Routing plex traffic through an SSH tunnel

This guide creates a reverse SSH tunnel to route all Plex server traffic through it.

Step 2 is done on the tunnel, all other steps are done on the plex server.

1. Setup SSH keys (if you already have key based authenthication setup skip to step 2)

On plex server:

1a. Create SSH key

root@ubuntu:~# ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Created directory '/root/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.

Passaphrase must be empy for autossh to work!

1b. Copy SSH key

root@ubuntu:~# ssh-copy-id root@TUNNELIP
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
root@TUNNELIP's password:

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'root@TUNNELIP'"
and check to make sure that only the key(s) you wanted were added.

1c. Connect to tunnel

root@ubuntu:~$ ssh root@TUNNELIP
Welcome to Ubuntu 16.04.1 LTS (GNU/Linux 4.9.7-x86_64-linode80 x86_64)
Last login: Wed Feb 22 03:49:58 2017
root@ubuntu:~#

You should not be promted for a password

2. Edit tunnel's SSH server configuration

2a. Add "Gatewayports yes" to sshd_config

root@ubuntu:~# nano /etc/ssh/sshd_config

Change:

...
Port 22
...

To:

...
Port 22
GatewayPorts yes
...

2b. restart sshd

sudo service ssh restart

3. Install autossh and create systemd service:

3a. Install autossh

sudo apt install autossh

3b. Create systemd service file

sudo nano /etc/systemd/system/autossh-plex-tunnel.service

Contents:

[Unit]
Description=AutoSSH tunnel service Plex on local port 32400
After=network.target

[Service]
Environment="AUTOSSH_GATETIME=0"

ExecStart=/usr/bin/autossh -M 40584 -o "compression=no" -o "cipher=aes128-gcm@openssh.com" -o "ServerAliveInterval 30" -o   "ServerAliveCountMax 3" -NR 32400:localhost:32400 root@TUNNELIP
User=changeme
[Install]
WantedBy=multi-user.target

4. Enable and start service

sudo systemctl enable autossh-plex-tunnel
sudo systemctl start autossh-plex-tunnel

4b. Check SSH tunnel

sudo systemctl status autossh-plex-tunnel

If tunnel was created successfully output should look something like this:

autossh-plex-tunnel.service - AutoSSH tunnel service Plex on local port 32400
   Loaded: loaded (/etc/systemd/system/autossh-plex-tunnel.service; enabled; vendor preset: enabled)
   Active: active (running) since Mon 2017-02-20 03:11:14 CET; 2 days ago
 Main PID: 32570 (autossh)
   CGroup: /system.slice/autossh-plex-tunnel.service
           ├─32570 /usr/lib/autossh/autossh -M 40584 -o compression=no -o cipher=aes128-gcm@openssh.com -o ServerAliveInterval 30 -o ServerAliveCountMax 3 -NR 32400:localhost:32400 root@TUNNELIP
           └─32574 /usr/bin/ssh -L 40584:127.0.0.1:40584 -R 40584:127.0.0.1:40585 -o compression=no -o cipher=aes128-gcm@openssh.com -o ServerAliveInterval 30 -o ServerAliveCountMax 3 -NR 32400:localhost:32400 root@TUNNELIP

Feb 20 03:11:14 Hetzner systemd[1]: Started AutoSSH tunnel service Plex on local port 32400.
Feb 20 03:11:14 Hetzner autossh[32570]: starting ssh (count 1)
Feb 20 03:11:14 Hetzner autossh[32570]: ssh child pid is 32574

go to http://TUNNELIP:32400 on your browser, if it does not load the tunnel was not setup correctly

5. Point plex.tv to correct ip

Plex.TV Web App > Settings > Server > Network > Custom server access URLs

https://TUNNELIP:32400,http://TUNNELIP:32400

6. Only allow local connections to port 32400

sudo iptables -A INPUT -p tcp -s localhost --dport 32400 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 32400 -j DROP
sudo iptables-save > /etc/iptables.rules

7. Make iptables rules apply at startup

edit /etc/network/interfaces

Change

auto  eth0
iface eth0 inet static
  address   xxx.xxx.xxx.xxx

To:

auto  eth0
iface eth0 inet static
 pre-up iptables-restore < /etc/iptables.rules
  address   xxx.xxx.xxx.xxx

Done!

Feel free to leave a comment with your questions or suggestions.

[Unit]
Description=AutoSSH tunnel service Plex on local port 32400
After=network.target
[Service]
Environment="AUTOSSH_GATETIME=0"
ExecStart=/usr/bin/autossh -M 40584 -o "compression=no" -o "cipher=aes128-gcm@openssh.com" -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -NR 32400:localhost:32400 root@TUNNELIP
User=root
[Install]
WantedBy=multi-user.target
@MonkeyCoin
Copy link

I keep getting these errors, anyone know why? I'm running on ubuntu 16.04

@MonkeyCoin
Copy link

Jan 31 04:05:41 vps autossh[27762]: starting ssh (count 13)
Jan 31 04:05:41 vps autossh[27762]: ssh child pid is 27913
Jan 31 04:05:41 vps autossh[27762]: Host key verification failed.
Jan 31 04:05:41 vps autossh[27762]: ssh exited with error status 255; restarting ssh
Jan 31 04:07:13 vps systemd[1]: Started AutoSSH tunnel service Plex on local port 32400.
Jan 31 04:07:41 vps systemd[1]: Started AutoSSH tunnel service Plex on local port 32400.
Jan 31 04:07:49 vps autossh[27762]: starting ssh (count 14)
Jan 31 04:07:49 vps autossh[27762]: ssh child pid is 27960
Jan 31 04:07:49 vps autossh[27762]: Host key verification failed.
Jan 31 04:07:49 vps autossh[27762]: ssh exited with error status 255; restarting ssh

@Ramblurr
Copy link

I'd like to create a setup like this with multiple tunnel boxes. I wonder if I just packed more urls into Plex.TV Web App > Settings > Server > Network > Custom server access URLs if it would work correctly? I'm guessing not.

@safusu
Copy link

safusu commented Sep 3, 2018

@MonkeyCoin If you ran ssh-keygen -t rsa as root, add the folder directory of root's .ssh folder to the autossh line. E.g. I ran as root, so my ExecStart line is as follows:

ExecStart=/usr/bin/autossh -M 40584 -o "compression=no" -o "cipher=aes128-gcm@openssh.com" -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -NR 32400:localhost:32400 root@TUNNELIP -i /root/.ssh/id_rsa

Also want to mention that you should disable remote access under Settings > Remote Access. Even after I added the iptables rules, content wasn't flowing through the tunnel. If I restart the server, that would've probably fixed my problems, but remote access should be disabled regardless.

@perfectly-preserved-pie
Copy link

I got to the end of this guide, however my entire server (not just Plex) became totally unreachable, even after disabling remote access.
When I logged into the server, I couldn't reach Google's homepage. I'm thinking this is a firewall issue but I don't know why exactly.

Is there something else besides editing /etc/network/interfaces I need to do?

@notlimahrelyt
Copy link

Quick question: After setting this up, my Tautulli application sees all streams as "Plex Relay" with a 127.0.0.1 address. Is it possible to use this method and still see the stream IPs? I run Tautulli scripts that rely on IP addresses of users.

Thank you!

@hamflix
Copy link

hamflix commented Apr 30, 2019

Can anyone lend a hand getting this process functional on Ubuntu 18.04?

Step 6/7 appears to be deprecated in 18.04 and i'm having a tough time finding a way to get it working.

@varoOP
Copy link

varoOP commented Nov 19, 2019

Quick question: After setting this up, my Tautulli application sees all streams as "Plex Relay" with a 127.0.0.1 address. Is it possible to use this method and still see the stream IPs? I run Tautulli scripts that rely on IP addresses of users.

Thank you!

Yo , I can help you there.
You just need to create reverse proxy of plexmediaserver.
I have used a domain , ssl certs and all which requires a few extra steps but you can just create a simple one completely ignoring the SSL part and only listening on port 80. - I don't recommend doing that at all for security reasons.
My config :

#Must be set in the global scope see: https://forum.nginx.org/read.php?2,152294,152294
#Why this is important especially with Plex as it makes a lot of requests http://vincent.bernat.im/en/blog/2011-ssl-session-reuse-rfc5077.html / https://www.peterbe.com/plog/ssl_session_cache-ab
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

#Upstream to Plex
upstream plex_backend {
    server 127.0.0.1:32400;
    keepalive 32;
}

server {
    #listen 80; i have my port 80 blocked on the firewall so nevermind this and i recommend not using http anyway, but you might need it to get your ssl certs from letsencrypt so uncomment it.
	listen 443 ssl; 
	server_name subdomain.domain.com;

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

	#Use letsencrypt.org to get a free and trusted ssl certificate
    ssl_certificate /etc/letsencrypt/live/subdomain.domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/subdomain.domain.com/privkey.pem;
	ssl_trusted_certificate /etc/letsencrypt/live/subdomain.domain.com/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 /etc/nginx/ssl/dhparam.pem;
	ssl_ecdh_curve secp384r1;

	#Will ensure https is always used by supported browsers which prevents any server-side http > https redirects, as the browser will internally correct any request to https.
	#Recommended to submit to your domain to https://hstspreload.org as well.
	#!WARNING! Only enable this if you intend to only serve Plex over https, until this rule expires in your browser it WONT BE POSSIBLE to access Plex via http, remove 'includeSubDomains;' if you only want it to effect your Plex (sub-)domain.
	#This is disabled by default as it could cause issues with some playback devices it's advisable to test it with a small max-age and only enable if you don't encounter issues. (Haven't encountered any yet)
	#add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

	#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 X-Forwarded-For $proxy_add_x_forwarded_for;
	proxy_set_header X-Forwarded-Proto $scheme;
	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;

	#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 http://plex_backend;
	}

}

Noob guide :
sudo apt update
sudo apt install nginx-full
sudo nano /etc/nginx/sites-enabled/plex.conf
Paste my config in this after you have modified it to your needs , ctrl + o to save and then ctrl+x to exit.
sudo nginx -t You should get a successful message here.
sudo systemctl restart nginx.service
And viola its not the local address anymore that shows up.

@maxisme
Copy link

maxisme commented Jan 3, 2020

Quick question: After setting this up, my Tautulli application sees all streams as "Plex Relay" with a 127.0.0.1 address. Is it possible to use this method and still see the stream IPs? I run Tautulli scripts that rely on IP addresses of users.
Thank you!

Yo , I can help you there.
You just need to create reverse proxy of plexmediaserver.
I have used a domain , ssl certs and all which requires a few extra steps but you can just create a simple one completely ignoring the SSL part and only listening on port 80. - I don't recommend doing that at all for security reasons.
My config :

#Must be set in the global scope see: https://forum.nginx.org/read.php?2,152294,152294
#Why this is important especially with Plex as it makes a lot of requests http://vincent.bernat.im/en/blog/2011-ssl-session-reuse-rfc5077.html / https://www.peterbe.com/plog/ssl_session_cache-ab
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

#Upstream to Plex
upstream plex_backend {
    server 127.0.0.1:32400;
    keepalive 32;
}

server {
    #listen 80; i have my port 80 blocked on the firewall so nevermind this and i recommend not using http anyway, but you might need it to get your ssl certs from letsencrypt so uncomment it.
	listen 443 ssl; 
	server_name subdomain.domain.com;

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

	#Use letsencrypt.org to get a free and trusted ssl certificate
    ssl_certificate /etc/letsencrypt/live/subdomain.domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/subdomain.domain.com/privkey.pem;
	ssl_trusted_certificate /etc/letsencrypt/live/subdomain.domain.com/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 /etc/nginx/ssl/dhparam.pem;
	ssl_ecdh_curve secp384r1;

	#Will ensure https is always used by supported browsers which prevents any server-side http > https redirects, as the browser will internally correct any request to https.
	#Recommended to submit to your domain to https://hstspreload.org as well.
	#!WARNING! Only enable this if you intend to only serve Plex over https, until this rule expires in your browser it WONT BE POSSIBLE to access Plex via http, remove 'includeSubDomains;' if you only want it to effect your Plex (sub-)domain.
	#This is disabled by default as it could cause issues with some playback devices it's advisable to test it with a small max-age and only enable if you don't encounter issues. (Haven't encountered any yet)
	#add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

	#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 X-Forwarded-For $proxy_add_x_forwarded_for;
	proxy_set_header X-Forwarded-Proto $scheme;
	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;

	#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 http://plex_backend;
	}

}

Noob guide :
sudo apt update
sudo apt install nginx-full
sudo nano /etc/nginx/sites-enabled/plex.conf
Paste my config in this after you have modified it to your needs , ctrl + o to save and then ctrl+x to exit.
sudo nginx -t You should get a successful message here.
sudo systemctl restart nginx.service
And viola its not the local address anymore that shows up.

I am getting the error:

Plex is not reachable.
Make sure your server has an internet connection and any firewalls or other programs are set to allow access.

when accessing my subdomain.domain.com?

@varoOP
Copy link

varoOP commented Jan 4, 2020

I am getting the error:

Plex is not reachable.
Make sure your server has an internet connection and any firewalls or other programs are set to allow access.

when accessing my subdomain.domain.com?

Is the SSH tunnel working for you without setting up a reverse proxy ?
Have you opened the necessary ports ?

There could be many reasons behind your error and I can't provide a solution unless we pinpoint the reason.

@maxisme
Copy link

maxisme commented Jan 4, 2020

Yes the ssh tunnel is working. Yes I have opened the necessary ports. My network is behind a CGNAT which is why I am doing this btw.

@sheryus
Copy link

sheryus commented Jan 4, 2020

hi the ssh tunnel work when i config my vps ip on client but if i not config manually the ip adress he doesnt work :(

@sheryus
Copy link

sheryus commented Jan 4, 2020

i lost the remote acces i can log on :(

@sheryus
Copy link

sheryus commented Jan 4, 2020

with putty tunnel dont work

@sheryus
Copy link

sheryus commented Jan 4, 2020

can you explain me Etap 6 and 7, why allow 32400 only local ?

@sheryus
Copy link

sheryus commented Jan 4, 2020

the tunnel ssh work if i don't execute etap 6 and 7, its dont work if i execute etap 6 and 7, remote acces to server red

@yimzhu1712
Copy link

Has anyone got this to work for emby?

I would love to get some tips on making this work. Thank you.

@pridit
Copy link

pridit commented Feb 3, 2020

the tunnel ssh work if i don't execute etap 6 and 7, its dont work if i execute etap 6 and 7, remote acces to server red

Step 6 and 7 will block access through the port unless it's being established through the tunnel. You need to access Plex through the tunnel address and not from where you have Plex hosted. Step 5 ensures Plex is doing this as well, you would be getting red if it can't establish a connection.

@maxisme
Copy link

maxisme commented Apr 28, 2020

Thank you so much again for this! Can you explain why you used -o compression=no -o cipher=aes128-gcm@openssh.com?

@adir6
Copy link

adir6 commented Sep 24, 2020

the tunnel ssh work if i don't execute etap 6 and 7, its dont work if i execute etap 6 and 7, remote acces to server red

Step 6 and 7 will block access through the port unless it's being established through the tunnel. You need to access Plex through the tunnel address and not from where you have Plex hosted. Step 5 ensures Plex is doing this as well, you would be getting red if it can't establish a connection.

Hello, I am getting an error. I have done everything as suggested.
sx-24-September-20-7646

@pridit
Copy link

pridit commented Sep 26, 2020

the tunnel ssh work if i don't execute etap 6 and 7, its dont work if i execute etap 6 and 7, remote acces to server red

Step 6 and 7 will block access through the port unless it's being established through the tunnel. You need to access Plex through the tunnel address and not from where you have Plex hosted. Step 5 ensures Plex is doing this as well, you would be getting red if it can't establish a connection.

Hello, I am getting an error. I have done everything as suggested.
sx-24-September-20-7646

Hard to diagnose without knowing anything about your setup. I've seen this error before with trying to transcode when the server disallows it. I don't think this has anything to do with the tunnel, unless you've mounted something in the cloud I guess.

@mrdet2333
Copy link

mrdet2333 commented Oct 25, 2020

Hi
I really appreciate your work and it really helps me a lot.
There is a step which confuses me. Is there any reason why we need to limit port 32400 only to local connection? (Step 6)

Regards

@D34DC3N73R
Copy link

You need to limit port 32400 to local to ensure the tunnel is being used. Plex will always try to access the plex installation directly if it can. When you get everything set up, you should only see local traffic in the dashboard.

@BEisem
Copy link

BEisem commented Oct 24, 2021

Noob guide : sudo apt update sudo apt install nginx-full sudo nano /etc/nginx/sites-enabled/plex.conf Paste my config in this after you have modified it to your needs , ctrl + o to save and then ctrl+x to exit. sudo nginx -t You should get a successful message here. sudo systemctl restart nginx.service And viola its not the local address anymore that shows up.

I know this is a 2-year old comment, so I don't know if you're still following this. I've tried this and I'm still getting local IPs in Tautulli. In fact according to the Tautulli developer, Tautulli gets all information from Plex. So if Plex is reporting local IPs, I don't see how Tautulli would report something else.

Just not sure if I'm missing something, because as it stands, I've followed these steps but can't get Tautulli to report anything other than the local IP.

@BEisem
Copy link

BEisem commented Nov 9, 2021

Me again ... /etc/network/interfaces no longer exists for modern versions of Ubuntu. It has been replaced by netplan.io. Does anyone know how to complete Step 7 using the newer netplan.io in Ubuntu 20.06?

@D34DC3N73R
Copy link

My box is still running ubuntu 18, which also has netoplan and I use a package called iptables-persistent to apply the rules on boot. There's also nftables which is relatively new.

@BEisem
Copy link

BEisem commented Nov 9, 2021

My box is still running ubuntu 18, which also has netoplan and I use a package called iptables-persistent to apply the rules on boot. There's also nftables which is relatively new.

Thanks, I also found iptables-persistent and installed it. I’ve got this working, but the only thing that isn’t working for me is the reverse proxy described here: https://gist.github.com/MarMed/94b5537a9fb61cf7212808692bbef14d#gistcomment-3087342. I really want to pass correct IPs to Plex, but they are still showing as local.

@ShipkaChalk
Copy link

Took some inspiration from this and created a dockerised version which uses Wireguard: https://gist.github.com/ShipkaChalk/629fdc42dad781776d2007fc502188f3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment