Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chromecast will connect directly to jellyfin, ignoring reverse proxies #601

Closed
hawken93 opened this issue Jan 16, 2019 · 19 comments
Closed
Labels
bug Something isn't working stale Stale and will be closed if no activity occurs

Comments

@hawken93
Copy link
Contributor

hawken93 commented Jan 16, 2019

Describe the bug
When you cast to a chromecast, the chromecast will try to connect to jellyfin. It does so by seemingly connecting to the IP of the jellyfin box at the jellyfin port. Even when jellyfin is served via a reverse proxy.

This works for most people, but it doesn't work for me because the wifi devices are not allowed to talk directly to the VM and must go through the reverse proxy.

To Reproduce

  1. own a chromecast
  2. set up a reverse proxy VM, use nginx or apache for example, there is documentation available for this
  3. set up a jellyfin VM but make it only accept connections to 8096 from the reverse proxy
  4. tcpdump on the jellyfin VM: tcpdump -ni <nic> port 8096 and not host <reverse proxy ip>
  5. visit jellyfin on the reverse proxy
  6. chromecast

Expected behavior
I would expect that the chromecast derives its url from the initiator device (chrome on android, for example). They are on the same layer 2 network and will probably have the same URL to reach things.

I can understand what I think is the current behavior:
Server determines if client ip is coming from rfc local addresses or from the internet, then:

  • LAN: Server sends its lan ip and port
  • WAN: Server sends its configured wan hostname and wan plain or tls port
    The server also probably sees if the client is on http or https to decide which of the wan ports to send.

I think this is made with one usecase in mind, the one where Jellyfin runs the http and https server in a home environment and the user has made port forwards in their router.

I started trying to show a few scenarios where this can go wrong, but the truth is that there are very many ways to break this functionality because it assumes a lot about your network and setup. I would like it more if Jellyfin tries to be agnostic about this and leave this task to the clients

Logs/Screenshots
will provide if asked

System

  • OS: Debian
  • proxy: apache2
  • Browser: Chrome Android/Windows
  • Jellyfin Version: 10.0.2

Additional context
I would gladly do the fix on this, but it would make my life easier if I could get some help to track down where in the code this logic sits and interacts with the client. I've found chromecast debugging to be something I really suck at.

Additionally, I think that Kodi uses Jellyfins autodetected WAN ip that was removed and then restored, so whatever we do, that must remain.

@hawken93 hawken93 added the bug Something isn't working label Jan 16, 2019
@hawken93
Copy link
Contributor Author

hawken93 commented Jan 17, 2019

I've come to realize that a http->https redirect with a 80 -> 8096 and 443 -> ??? port mapping in the simple NAT router scenario explains perfectly why things are how they are. If this is to be rethinked, I can see two options:

try to get away with just jellyfin, no reverse proxy
jellyfin can't bind to ports < 1024 easily in linux, so that would create a reason for someone to not only port forward, but also change the port in the router. In this case, the ssl redirect to the local ssl port would fail. To avoid that we use the separate WAN ssl port setting so that it can redirect to that one instead. But jellyfin can't know which one is right.. so therein comes the ip filtering and guesswork to figure out if the user is on LAN and can connect directly or if it needs to account for the port mapping.

jellyfin in docker gets nginx reverse proxy
nginx can bind to ports < 1024 and in that case we don't need to support that kind of port mapping, in this case jellyfin doesn't even need to know how to port redirect. Then those settings would stop being in the GUI and the user would need to configure nginx following documentation. Not sure how people would like that

The funny thing is that if one were to drop the idea of a https redirect, it would always be the case that if you are connected already, the server wouldn't need to know your port mapping parameters.

@joshuaboniface
Copy link
Member

Interestingly, I can't seem to confirm this, though my config is more complex:

10.X.0.0/24    |    10.Y.0.0/24
Clients -> Firewall -> HAProxy Reverse Proxy server -> Jellyfin server 

Firewall blocks 8096 through it from 10.X.0.0 to 10.Y.0.0, and confirmed via tshark on the Jellyfin server that no traffic was coming from the client network.

PORT     STATE    SERVICE
8096/tcp filtered unknown

Clients tested: Chrome on Android, Jellyfin Android debug app

The reverse proxy listens on 443, strips SSL, passes to a (second) server running Jellyfin on port 8096.

I'm also doing a forced redirect on the HAProxy instance from 80 to 443.

I can try some testing with nginx at some point soon, but more confirmations would be helpful. Also can you post the nginx configuration? I don't doubt it's doing funky things with the traffic/URLs but it doesn't seem to cause any issues in my setup.

@hawken93
Copy link
Contributor Author

hawken93 commented Jan 17, 2019

<VirtualHost *:80>
        ServerName emby.example.org
        ServerAdmin ...

        Include /etc/apache2/conf-available/acme-setup.conf
</VirtualHost>

<VirtualHost *:443>
        ServerName emby.example.org
        ServerAdmin ...

        SSLEngine on
        SSLCertificateFile       /etc/letsencrypt/live/jellyfin.example.org/cert.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/jellyfin.example.org/privkey.pem
        SSLCACertificateFile  /etc/letsencrypt/live/jellyfin.example.org/chain.pem
        Header always set strict-transport-security "max-age=63072000; preload"

        ProxyPreserveHost On
        ProxyPass        / http://jellyfin.vm.int:8096/
        ProxyPassReverse / http://jellyfin.vm.int:8096/
        RewriteEngine On
        RewriteRule /embywebsocket(\?.*)? ws://jellyfin.vm.int:8096/embywebsocket$1 [P,QSA]
        RewriteRule /socket(\?.*)?        ws://jellyfin.vm.int:8096/socket$1 [P,QSA]
</VirtualHost>

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

obviously replaced some names but this is the apache2 config :) But the "emby" name is there on purpose, because I still haven't switched and because I suspect that javascript may be searching for the string and getting tricked like I discovered relating to websockets. The acme setup config is a shorthand I use multiple times for redirecting but also letting a directory on the server be served for letsencrypt acme setup.

I wanted to ask because it didn't seem too clear to me, the chromecast connected and worked for you, right? If so, then maybe it's apache2, or maybe it's because there is some javascript that is searching for emby in my domain name and therefore decides that it's entirely rational to behave differently if there is an "emby" in the url :\ idk

This server is my "production" instance so that's the reason for using 10.0.2. I haven't gotten around to test dev with this setup. Might do soon, though. :)

also leaving a link in here for jellyfin-archive/jellyfin-docs#2 which is an issue that has to do with documenting the apache2 best practices :) I'll be replacing my Rewrite magic with normal ProxyPass magic in due time :)

@furyfire
Copy link

Confirmed issue. Running Jellyfin behind a reverse proxy and my own custom subdomain.

The https:///emby/System/Info endpoint reports the wrong IP address and port for the internal address
(Docker IP and port 8096 )

The external adresse is my correct external IP address and port 8096.

This external/internal address should be possible to overwrite through the configuration file.

@hawken93
Copy link
Contributor Author

hawken93 commented Jan 28, 2019

@joshuaboniface I'd be interested to ask what your System/Info endpoint returns, mine is consistent with @furyfire's findings, and if yours is somehow different it could explain things :)

@Phlogi
Copy link
Contributor

Phlogi commented Mar 24, 2019

I can confirm this issue too. The WAN IP is not announced persistently, which probably has to do with the external service (icanhazip.com) being used currently.
In this pull request however there is another bug: The port being used with the WAN Address is not the public port but always the internal/local http (!) port is used, see the function call here. This port is also displayed on the dashboard page.

This needs to be changed to Public https port number, not sure how to access that in the mentioned file.

Also I think a config option to always show the DNS name instead of the WAN address would make sense. What do you think?

@hawken93
Copy link
Contributor Author

hawken93 commented Mar 24, 2019

I think the whole mess should be removed entirely so the clients can figure out how to connect without the help of this api thing. I know it would not be trivial, but worth it. A good start is to leave the mess in place but refactor it out of supported clients.

@Phlogi
Copy link
Contributor

Phlogi commented Mar 24, 2019

I think the whole mess should be removed entirely so the clients can figure out how to connect without the help of this api thing. I know it would not be trivial, but worth it. A good start is to leave the mess in place but refactor it out of supported clients.

This is not clear for me anyway: The client needs to know the address of the endpoint to query the system information, so it actually has the DNS name or the IP already. We could simplify this part by just keeping the DNS name if available in the Configuration? Variable is: WanDdns

@Phlogi
Copy link
Contributor

Phlogi commented Mar 24, 2019

I added another commit to change the behavior: https://github.com/jellyfin/jellyfin/pull/1151/commits

This code is untested, anyone willing to test it? :)

@hawken93
Copy link
Contributor Author

hawken93 commented Mar 24, 2019

That PR looks fine and all, but I still stand firmly on the side of "client already connected, api is unnecessary"

Specifically, the client already knows how to query the server, the server trying to tell the client where it would be nice to connect is stupid :(

@anthonylavado anthonylavado added this to Needs triage in Issue Triage for Main Repo Jul 4, 2019
@stale
Copy link

stale bot commented Jul 29, 2019

Issues go stale after 60d of inactivity. Mark the issue as fresh by adding a comment or commit. Stale issues close after an additional 7d of inactivity. If this issue is safe to close now please do so. If you have any questions you can reach us on Matrix or Social Media.

@stale stale bot added the stale Stale and will be closed if no activity occurs label Jul 29, 2019
@stale stale bot closed this as completed Aug 5, 2019
Issue Triage for Main Repo automation moved this from Needs triage to Closed/Done Aug 5, 2019
@mooninite
Copy link

This is still an issue on the 10.4 branch, but I have found a workaround using Apache and mod_substitute. Add the following to your reverse proxy.

<VirtualHost *:443>
        ServerName jellyfin.example.com
        # Important for Substitute
        RequestHeader unset Accept-Encoding
        SSLEngine on
        ... other SSL settings go here ...

        <Location "/">
        ProxyPass "http://jellyfin.example.com:8096/"
        ProxyPassReverse "http://jellyfin.example.com:8096/"
        AddOutputFilterByType SUBSTITUTE application/json
        Substitute s|"LocalAddress":"(.*?)"|"LocalAddress":"https://jellyfin.example.com"|i
        </Location>
</VirtualHost>

The Chromecast properly connects to the HTTPS host address using DNS.

Current 10.4 behavior:

  • Secure connection mode set to "Disabled" publishes "LocalAddress":"http://[WAN IP address]:8096" - works fine with a Chromecast on a local network or if you port forward - but this is unencrypted traffic.
  • Secure connection mode set to "Handled by reverse proxy" publishes "LocalAddress":"https://[LAN IP address]:8920" -- which will NEVER work. Chromecasts only trust global CA signed certs.

A proper fix would involve sending the correct DNS address and NOT the IP address in the https://jellyfin.example.com/System/Info/Public JSON response. A toggle to switch DNS address on/off may be desired if users don't have a public DNS host name.

@petriusus
Copy link

This is still an issue on the 10.4 branch, but I have found a workaround using Apache and mod_substitute. Add the following to your reverse proxy.

<VirtualHost *:443>
        ServerName jellyfin.example.com
        # Important for Substitute
        RequestHeader unset Accept-Encoding
        SSLEngine on
        ... other SSL settings go here ...

        <Location "/">
        ProxyPass "http://jellyfin.example.com:8096/"
        ProxyPassReverse "http://jellyfin.example.com:8096/"
        AddOutputFilterByType SUBSTITUTE application/json
        Substitute s|"LocalAddress":"(.*?)"|"LocalAddress":"https://jellyfin.example.com"|i
        </Location>
</VirtualHost>

The Chromecast properly connects to the HTTPS host address using DNS.

Current 10.4 behavior:

* Secure connection mode set to "Disabled" publishes "LocalAddress":"http://[WAN IP address]:8096" - works fine with a Chromecast on a local network or if you port forward - but this is unencrypted traffic.

* Secure connection mode set to "Handled by reverse proxy" publishes "LocalAddress":"https://[LAN IP address]:8920" -- which will NEVER work. Chromecasts only trust global CA signed certs.

A proper fix would involve sending the correct DNS address and NOT the IP address in the https://jellyfin.example.com/System/Info/Public JSON response. A toggle to switch DNS address on/off may be desired if users don't have a public DNS host name.

Reset "Accept-Encoding" worked for redbird proxy too.

Thank you!

@bengalih
Copy link

This is still an issue on the 10.4 branch, but I have found a workaround using Apache and mod_substitute. Add the following to your reverse proxy.

<VirtualHost *:443>
        ServerName jellyfin.example.com
        # Important for Substitute
        RequestHeader unset Accept-Encoding
        SSLEngine on
        ... other SSL settings go here ...

        <Location "/">
        ProxyPass "http://jellyfin.example.com:8096/"
        ProxyPassReverse "http://jellyfin.example.com:8096/"
        AddOutputFilterByType SUBSTITUTE application/json
        Substitute s|"LocalAddress":"(.*?)"|"LocalAddress":"https://jellyfin.example.com"|i
        </Location>
</VirtualHost>

The Chromecast properly connects to the HTTPS host address using DNS.

Current 10.4 behavior:

  • Secure connection mode set to "Disabled" publishes "LocalAddress":"http://[WAN IP address]:8096" - works fine with a Chromecast on a local network or if you port forward - but this is unencrypted traffic.
  • Secure connection mode set to "Handled by reverse proxy" publishes "LocalAddress":"https://[LAN IP address]:8920" -- which will NEVER work. Chromecasts only trust global CA signed certs.

A proper fix would involve sending the correct DNS address and NOT the IP address in the https://jellyfin.example.com/System/Info/Public JSON response. A toggle to switch DNS address on/off may be desired if users don't have a public DNS host name.

@mooninite - Hey. I'm interested in your follow-up comments regarding this post and my response to your similar issue a few days later here:
#1396 (comment)

Based on the testing I describe there it would be clear to me that Chromecast must be properly communicating with NGINX since NGINX is the only path into my JF server over the remote network. I would like to know what you think about my testing and how that compares to the issues you describe. thanks.

@Avamander
Copy link

I'm still seeing "LocalAddress":"http://[docker internal IP]:8096" on 10.8.7 (3c777f091042), is there no good way to override the value sent? Searching for LocalAddress just sent me to this issue.

@PrplHaz4
Copy link
Contributor

PrplHaz4 commented Nov 6, 2022

I'm still seeing "LocalAddress":"http://[docker internal IP]:8096" on 10.8.7 (3c777f091042), is there no good way to override the value sent? Searching for LocalAddress just sent me to this issue.

I believe PublishedServerUrl used to take care of this, but there have been some changes recently, and I'm not sure if that's still true...

@Avamander
Copy link

Avamander commented Nov 6, 2022

I believe PublishedServerUrl used to take care of this, but there have been some changes recently, and I'm not sure if that's still true...

"ManualAddress":"https://[actual URL]","LocalAddress":"http://[docker internal IP]:8096" and it seems to work better, I will have to test, but I think is sometimes incorrectly used as a fallback (which will never work, because it's both a private docker IP and mixed content is forbidden).

@mooninite
Copy link

The documentation is not very clear but the setting is in Advanced > Networking > Firewall and Proxy Settings > Published Server URIs. The correct value in the box is "external=https://<FQDN/IP for chromecast>" so if your external URL is "foobar.jellyfin.example.com you need to put in "external=https://foobar.jellyfin.example.com".

@ginger-tek
Copy link

The documentation is not very clear but the setting is in Advanced > Networking > Firewall and Proxy Settings > Published Server URIs. The correct value in the box is "external=https://<FQDN/IP for chromecast>" so if your external URL is "foobar.jellyfin.example.com you need to put in "external=https://foobar.jellyfin.example.com".

Would have loved for this to fix my issue, but alas, I still cannot get chromecast to work when using a reverse proxy; only raw local IP works :(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working stale Stale and will be closed if no activity occurs
Projects
Development

No branches or pull requests

10 participants