Improving remote access security: verifying TLS SNI with haproxy (feature request?)

Anyone browsing the Plex server port (default 32400) can see it’s running a Plex server - there’s a big splash screen and you can view all the plex.tv hosted (free) content.

There’s not much reason to show so much information to anyone scanning your router.

It may not be a good idea to allow remote access at all without a VPN, but security can still be improved. To help limit access to legitimate clients I setup haproxy to verify the plex.direct TLS certificate.

IMO this should be an option in the Plex server - the server already knows its certificate

Background: every Plex server is issued a wildcard certificate

Plex clients connect to https://x-x-x-x.randomhash.plex.direct:public-serverport
x-x-x-x = any IPv4 address x.x.x.x

The randomhash is presumably unique to your Plex server certificate.
Anyone can find it by browsing to the https port and looking at the certificate information.

If your server has been online with remote access for any length of time, it’s possible the TLS certificate has been fingerprinted and in a database somewhere - there are a lot of online vulnerability scanners, not all of them run by “good guys”.

Of course your ISP or anyone who can monitor your traffic can see the TLS servername in the ClientHello phase before the encryption starts.

It may be very easy to find this hash without logging in to plex.tv with a web or app but I’m hoping this isn’t the case.

With all those disclaimers aside, it may be useful to block who can see your Plex server.

Here’s a sample haproxy (1.8) config that redirects Plex clients looking the the plex.direct hostname to the back end Plex server. Any other TLS connectons go to a default backend server.

frontend plex-https-32443
   bind *:32443   # change to your preferred external port number
   mode tcp
   tcp-request inspect-delay 5s
   tcp-request content accept if { req_ssl_hello_type 1 }
# change to match the 32 character hash in the plex.direct cert
   use_backend backend_plex if { req.ssl_sni -m end .xxxxxchangemexxxxxxxxxxxxxxxxxxx.plex.direct }
   default_backend bk_ssl_default
 
backend backend_plex
   mode tcp
   balance roundrobin
   server server-plex 127.0.0.1:32400 check

backend bk_ssl_default
   mode tcp
   balance roundrobin
   server server-whatever 127.0.0.1:443 check
`Preformatted text

Just noticed it recently, But any connections should be verified to match either the host and the certificate, So in the end even if they connect to the port the server just responds with 404 or not even respond unless it has a proper authenticated response, TLS SNI should also be at the plex level too and not just for anyone running any type of reverse proxy. This would be a good thing to have added just in general.

It’s not a wildcard cert. It is an individual certificate just for your server and is “pinned” to the FQDN of your server, which is assigned to this individual server as soon as you connect it to your Plex account.
If you “require” secure connections in the server config, no client is allowed to fall back to unencrypted communication.

Which means: while it may still be possible for an outsider to load the web app unencrypted, it cannot do much with it. Because the server will refuse further communication when it comes to accessing your actual media content.

Of course it’s a wildcard cert - it’s right there in the CN of the certificate which you can see for yourself using OpenSSL or almost any browser:

*..plex.direct.

It’s also very well explained in the link I provided so I’m not going to repeat how Plex certificates work.

This has nothing to do with bypassing encryption. Plex will happily respond with very good TLS 1.3 encryption to anyone port scanning your public IP.

Once the attacker knows it’s Plex, they may be able to exploit that.

The HAProxy config adds a bit of extra security by hiding the Plex server from scanners while letting registered Plex clients connect. You don’t have to use it.

I don’t think you did read the linked blog post carefully: How Plex is doing HTTPS for all its users

While it is true that app.plex.tv presents a wildcard cert *.plex.tv for loading the web app.
But your personal Plex server presents an individual cert, which is pinned to the individual hashed ID of your server.
Which is explained in the blog, with an example of *.625d406a00ac415b978ddb368c0d1289.plex.direct.

Granted.

Thanks for confirming exactly what I said in the first place!

The individual hashed wildcard certificate all by itself exposes to anyone that you are running Plex. The Plex Media Server also exposes web content.

The haproxy config hides the plex certificate and media server port from random scanning.

In this sample, anyone browsing to port 32443 gets connected to a default web server. I currrently have it redirected to openvpn but you could redirect or block it.

Only authorized Plex apps/clients can connect to the Plex server with this config because they know the hash. (It’s called CertificateUUID in Preferences.xml)

In Plex remote access, I configured port 32443 as the manual public port and it connects to port 32400 internally. Firewall configuration permits only port 32443

from /etc/haproxy/haproxy.cfg

frontend www-https-32443
    bind x.x.x.x:32443   # port exposed to public
    mode tcp
    tcp-request inspect-delay 5s
    tcp-request content accept if { req_ssl_hello_type 1 }
    use_backend backend_plex if { req.ssl_sni -m end .xxxplexuuidxxx.plex.direct }
    default_backend bk_ssl_default

backend backend_plex
   mode tcp
   balance roundrobin
   server server-plex 127.0.0.1:32400 check

backend bk_ssl_default
   mode tcp
   balance roundrobin
   server server-ovpn 127.0.0.1:443   # default web server

The configuration can be checked with openssl from outside your firewall:

openssl s_client -connect your.public.ip:32443  -quiet

depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = R3
verify return:1
depth=0 CN = yourserver.whatever.com
verify return:1


openssl s_client -connect your.public.ip:32443  -quiet -servername whatever.xxxxyourplexxcertificateUUID.plex.direct

depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert High Assurance EV Root CA
verify return:1
depth=1 C = US, O = "Plex, Inc.", CN = Plex Devices High Assurance CA3
verify return:1
depth=0 C = US, ST = California, L = Los Gatos, O = "Plex, Inc.", CN = *.xxxxyourplexxcertificateUUID.plex.direct
verify return:1