Unknown Bug with Remote Access Apps (Roku, Android,etc) - Architectural Guidance with Proxy/Cloudflare

So I’ve solved my issues!

To recap - I initially was unable to establish a direct connection to my server through NGINX using Roku/Android app. This is detailed here. My first solution (detailed in that thread) was to move my DNS over to Cloudflare and use their “proxy” DNS which appeared to properly work. I was however left with a mystery as to why I was required to use their proxy option instead of just hitting my NGINX server directly.

I figured out how to do “debug” level logging on NGINX as well as how to get a local tcpdump on my android phone while attempting access to my IP directly. This provided some good evidence that this was some type of certificate issue:

From tcpdump:

377	9.746316	100.XX.XX.XX	70.XX.XX.XX	TLSv1.2	75	Alert (Level: Fatal, Description: Certificate Unknown)

From nginx log:

2020/01/14 05:32:27 [debug] 15340#0: *1403 SSL certificate status callback
2020/01/14 05:32:27 [debug] 15340#0: *1403 SSL ALPN supported by client: http/1.1
2020/01/14 05:32:27 [debug] 15340#0: *1403 SSL ALPN selected: http/1.1
2020/01/14 05:32:27 [debug] 15340#0: *1403 SSL_do_handshake: -1
2020/01/14 05:32:27 [debug] 15340#0: *1403 SSL_get_error: 2
2020/01/14 05:32:27 [debug] 15340#0: *1403 reusable connection: 0
2020/01/14 05:32:27 [debug] 15340#0: *1403 SSL handshake handler: 0
2020/01/14 05:32:27 [debug] 15340#0: *1403 SSL_do_handshake: -1
2020/01/14 05:32:27 [debug] 15340#0: *1403 SSL_get_error: 1
**2020/01/14 05:32:27 [info] 15340#0: *1403 SSL_do_handshake() failed (SSL: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown:SSL alert number 46) while SSL handshaking, client: 172.58.99.136, server: 0.0.0.0:443**
2020/01/14 05:32:27 [debug] 15340#0: *1403 close http connection: 3
2020/01/14 05:32:27 [debug] 15340#0: *1403 event timer del: 3: 211227619
2020/01/14 05:32:27 [debug] 15340#0: *1403 reusable connection: 0
2020/01/14 05:32:27 [debug] 15340#0: *1403 free: 001BC360, unused: 8
2020/01/14 05:32:28 [debug] 15341#0: *1407 SSL certificate status callback
2020/01/14 05:32:28 [debug] 15341#0: *1407 SSL ALPN supported by client: http/1.1
2020/01/14 05:32:28 [debug] 15341#0: *1407 SSL ALPN selected: http/1.1
2020/01/14 05:32:28 [debug] 15341#0: *1407 SSL_do_handshake: -1
2020/01/14 05:32:28 [debug] 15341#0: *1407 SSL_get_error: 2
2020/01/14 05:32:28 [debug] 15341#0: *1407 reusable connection: 0

At this point I was pretty clear it was a certificate error though I didn’t exactly understand how it could be. After all when accessing the same URL via my browser I had a fully validated certificate chain. At first I thought that the Android client was somehow hard-coded with certain root CAs and was using them instead of honoring the system certificates. This was not the case, although the Android client does seem more sensitive to some SSL configuration parameters than any browser.

The answer comes down to the description of the ssl_certificate option in the NGINX documentation:

“Specifies a file with the certificate in the PEM format for the given virtual server. If intermediate certificates should be specified in addition to a primary certificate, they should be specified in the same file in the following order: the primary certificate comes first, then the intermediate certificates. A secret key in the PEM format may be placed in the same file.”

My ssl_certificate pem file was simply my server certificate as generated by Let’s Encrypt. I did have the LE intermediate certificate specified in the ssl_trusted_certificate directive, but apparently that was not sufficient. I created a new .pem file by concatenating the two together and used that new certificate as my ssl_certificate.
After reloading NGINX, my issues were solved!

I’m surprised that I was unable to track down the answer from other people’s configuration instructions. Not a single configuration guide I read (and I read dozens) mentioned configuring the certificate in this way. That being said, quite a few people were either:

  • Not using the android/roku clients in their configurations and if they were they probably didn’t disable Plex remote access and were actually being relayed through plex direct.
  • Using Cloudflare proxy (most common), in which case they would be presenting the CF cert to the client which apparently is configured properly so as to not cause this issue.

I really hope this helps someone else down the road!

1 Like