Plex, HTTPS, and headaches

*google searches 'wiki plex'*

Plex is a client-server media player system and software suite comprising two main components. The Plex Media Server (PMS) desktop application runs on Windows, macOS and Linux-compatibles including some types of NAS devices.

It's a pretty common media server, with its primary client application being a web browser which connects to their servers:

$ curl -vsL https://app.plex.tv/desktop > /dev/null
*   Trying 23.22.246.0...
* TCP_NODELAY set
* Connected to app.plex.tv (23.22.246.0) port 443 (#0)
* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate: *.plex.tv
* Server certificate: DigiCert SHA2 Secure Server CA
* Server certificate: DigiCert Global Root CA
> GET /desktop HTTP/1.1
> Host: app.plex.tv
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK  
< Date: Fri, 03 Nov 2017 04:44:51 GMT  
< Content-Type: text/html; charset=utf-8  
< Transfer-Encoding: chunked  
< Connection: keep-alive  
< Server: nginx/1.13.0  
< X-Frame-Options: ALLOW-FROM http://app.plex.tv  
< X-XSS-Protection: 1; mode=block  
< X-Content-Type-Options: nosniff  
< Content-Security-Policy: frame-ancestors 'self' http://app.plex.tv  
< ETag: W/"11cffa0d5e7691c70e21385567c89d97"  
< Cache-Control: max-age=0, private, must-revalidate  
< X-Request-Id: 181266ee-6aec-4d17-b52a-2f54abce9984  
< X-Runtime: 0.007836  
<  
{ [6689 bytes data]
* Connection #0 to host app.plex.tv left intact

This resolves to a web server at 23.22.246.0, with an HTTPS certificate presented as *.plex.tv.

However...

The web server informs the client (your browser) of some new hostnames to which follow-up requests are sent. I'll have to find a nice way to present this proof, but for the time being go ahead and open your browser's dev tools and watch all the subsequent requests after https://app.plex.tv/desktop loads.

A slightly-redacted sample of abbreviated requests consists of the following:

  1. https://plex.tv:443/
  2. wss://pubsub.plex.tv:443/
    • (Need to confirm port)
  3. https://192-168-1-14.d0bdefd745cb45789a30d377f4162afd.plex.direct:32400/
  4. https://plex.lolnope.us:443/
  5. https://67-186-228-45.62988ef8f7084969939e9dd428e7c246.plex.direct:32400/
  6. https://192-168-10-51.62988ef8f7084969939e9dd428e7c246.plex.direct:32400/

plex.tv has us load pubsub.plex.tv which instructs your browser to make multiple follow-up requests for server discovery. Any PMS server can be reached on its LAN address, and can also be enabled for remote (WAN) access. If remote access is configured, there are two potential ways to connect. Also, if you share access to other PMS's, they will need to be discovered.

In the list I showed above, lines 3 through 6 show this discovery process for my PMS as well as a shared PMS. Each domain resolves to a proper IP context:

$ host 67-186-228-45.62988ef8f7084969939e9dd428e7c246.plex.direct 8.8.8.8
Using domain server:  
Name: 8.8.8.8  
Address: 8.8.8.8#53  
Aliases:

67-186-228-45.62988ef8f7084969939e9dd428e7c246.plex.direct has address 67.186.228.45

$ host 192-168-10-51.62988ef8f7084969939e9dd428e7c246.plex.direct 8.8.8.8
Using domain server:  
Name: 8.8.8.8  
Address: 8.8.8.8#53  
Aliases:

192-168-10-51.62988ef8f7084969939e9dd428e7c246.plex.direct has address 192.168.10.51  

This means if you're on your internal LAN, you'll connect to 192.168.10.51, whereas if you're out on the WAN, you'll connect to 67.186.228.45.

(You may have to disable DNS rebinding attack prevention).

HTTPS for all!

These domains may resolve to IPs all fine and dandy, but you may notice that each is being access via https. In particular, how can Plex (the company) issue a publicly valid certificate for a machine on your network?!

Well, check it:

$ openssl s_client -connect 67-186-228-45.62988ef8f7084969939e9dd428e7c246.plex.direct:32400 < /dev/null
CONNECTED(00000003)  
---
Certificate chain  
 0 s:/C=US/ST=CA/L=Los Gatos/O=Plex, Inc./CN=*.62988ef8f7084969939e9dd428e7c246.plex.direct
   i:/C=US/O=Plex, Inc./CN=Plex Devices High Assurance CA2
 1 s:/C=US/O=Plex, Inc./CN=Plex Devices High Assurance CA2
   i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV Root CA
---
Server certificate  
-----BEGIN CERTIFICATE-----
MIIE3jCCA8agAwIBAgIQDAa1VnGuiJqKTq8+6ncfQTANBgkqhkiG9w0BAQsFADBM  
MQswCQYDVQQGEwJVUzETMBEGA1UEChMKUGxleCwgSW5jLjEoMCYGA1UEAxMfUGxl  
eCBEZXZpY2VzIEhpZ2ggQXNzdXJhbmNlIENBMjAeFw0xNjEyMjUwMDAwMDBaFw0x  
NzEyMjUxMjAwMDBaMHwxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UE  
BxMJTG9zIEdhdG9zMRMwEQYDVQQKEwpQbGV4LCBJbmMuMTcwNQYDVQQDDC4qLjYy  
OTg4ZWY4ZjcwODQ5Njk5MzllOWRkNDI4ZTdjMjQ2LnBsZXguZGlyZWN0MIIBIjAN  
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqrQidEr1NhJl+36mwhpVGJIGdVze  
1szlypN8htt5GlPfSqoxJkagX5lLNK4graWXT+Xlg/Z+oMmJ4TAuZ7tQAIXOGgiD  
Y1klDAFkuwpE9aJGMEbNVEgCab2cA7Ve5CkkyrnOrtwM/JjJa/CXtJffXgRJMbZx  
lWhc30zZz6X2FYo6eawAbgBF/IIOMyp34O7o0z+s8fhg2vneDzxVEVbWjDOUL8mj  
+Xb2BJ1xLNYzdKQuBAcdRj7yiAZnFbGcxn+pqd0slG09wSzZOgYa2fN0UrvaRLzZ
WeF0V8Ocl1qFXRTlvwBIDY4yPAo8CTdupDE+qd3j8o58CUrQYln5qKqKTwIDAQAB  
o4IBijCCAYYwHwYDVR0jBBgwFoAUlIuJ90hyifJRStmIe+Vhtaqc1QEwHQYDVR0O  
BBYEFP7KCZF0qapJGFgVZpKZh8E7NXvHMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUE  
FjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwTAYDVR0gBEUwQzA3BglghkgBhv1sAQEw  
KjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAIBgZn  
gQwBAgIwfgYIKwYBBQUHAQEEcjBwMCUGCCsGAQUFBzABhhlodHRwOi8vb2NzcHgu  
ZGlnaWNlcnQuY29tMEcGCCsGAQUFBzAChjtodHRwOi8vY2FjZXJ0cy5kaWdpY2Vy  
dC5jb20vUGxleERldmljZXNIaWdoQXNzdXJhbmNlQ0EyLmNydDAMBgNVHRMBAf8E  
AjAAMDkGA1UdEQQyMDCCLiouNjI5ODhlZjhmNzA4NDk2OTkzOWU5ZGQ0MjhlN2My  
NDYucGxleC5kaXJlY3QwDQYJKoZIhvcNAQELBQADggEBAHIjf/vMdbdBwi44J0zT  
1vN5CLVMsMQ1ceLJ4g8IwE5hD5cRxNUrL/WIsg6f42y9hvihvATEhzPfGM5sJkyg  
SVk/E1iYPXaNaWSXoLzbNcWRYQIGgQrpS1Tc21pE2mUHkwQcqthHCppDAr6BMGtx  
lPGA5hojVfeEmxbQvrPZuYh7fijV18A5059JJm4qNtYIsTpEWLUV3R9ra9ZemWh1  
5g2ydY/lEM1f833GXq0qNyvXkfV4p8hm8lXKqKRTGbh1hYOSodosY24+TTzbGBg6  
0W4n1RIsfFJzyHXx0d2wegAA7usYh3cfPKJHlkEmG4e993ChLrOFifCf99jEhis+  
W2E=  
-----END CERTIFICATE-----
subject=/C=US/ST=CA/L=Los Gatos/O=Plex, Inc./CN=*.62988ef8f7084969939e9dd428e7c246.plex.direct  
issuer=/C=US/O=Plex, Inc./CN=Plex Devices High Assurance CA2  
---
No client certificate CA names sent  
---
SSL handshake has read 3421 bytes and written 456 bytes  
---
New, TLSv1/SSLv3, Cipher is DHE-RSA-AES128-SHA  
Server public key is 2048 bit  
Secure Renegotiation IS supported  
Compression: NONE  
Expansion: NONE  
SSL-Session:  
    Protocol  : TLSv1
    Cipher    : DHE-RSA-AES128-SHA
    Session-ID: 396EC8610AFB13EFE30A1472821AC7EB2A7822AD9FC28552BAA94326CF2A3EE7
    Session-ID-ctx: 
    Master-Key: E429EDCB9170E007207EE3BDA01E2C0768B563DB405EB439954F73312AA02021E3196AFCBB12718EFCBED98C0EB15FCE
    Key-Arg   : None
    Start Time: 1509687469
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)
---

Plex (the company) generates *.62988ef8f7084969939e9dd428e7c246.plex.direct wildcard certificates for each PMS/user out there, and the certificates must be synced down to PMS at some point so the PMS web server can present valid HTTPS certificates.

(The internal domain would have presented the same cert, but I cannot reach 192.168.10.51 from WAN. You'll have to trust me.)


Mkay, what now

I wanted to have my own domain, https://plex.lolnope.us, to reach instead of going to https://app.plex.tv. This implies I must have a valid HTTPS certificate if I didn't want browsers getting all upset. 😤

Let's Encrypt is an easy way to do this. Luckily, they have a pfSense package to help with this.

After having a proper HTTPS certificate, I needed to route HTTPS requests via SNI so I can run multiple HTTPS sites on a single IP. HAproxy has a pfSense package that can do this. PMS has a setting known as Custom server access URLs which will publish back to plex.tv the domain names by which your server is accessible.

Now that I can route Plex HTTPS traffic, I can expose TCP port 443 as an HAproxy frontend, and route traffic to TCP port 32400 on the backend. To instruct plex.tv to send requests to TCP port 443, PMS has a setting known as Remote Access Public Port which I manually specified as 443.

Damn mobile app

The last hurdle was that the (iOS) mobile app seems to be hardcoded to use TCP port 32400 when accessing my WAN IP. So I still needed to add an HAproxy frontend on TCP 32400 and decrypt with the same plex.lolnope.us certificate I'm using for TCP port 443.


Result

I can now browse to plex.lolnope.us and access my content on a web browser or mobile app, on my LAN or on WAN. 👍