home-assistant / architecture

Repo to discuss Home Assistant architecture
317 stars 99 forks source link

Ability to host the application on a path #156

Closed davidjb closed 5 years ago

davidjb commented 5 years ago

As first discussed at https://github.com/home-assistant/home-assistant/issues/21113, Home Assistant doesn't currently support hosting on a path (eg https://home.example.com/homeassistant), being only able to be hosted at the root of a domain.

Being able to serving Home Assistant from a subdirectory would allow it to co-exist alongside several other applications on an existing single hostname (served by a web server or reverse proxy, like Nginx) . This would save maintaining a separate hostname for this one application, removing the need for additional firewall, DNS configuration or HTTPS certificate config/renewals and so on.

In other applications, serving an application on a path is common. It usually involves the app's resources being served with either absolute URLs (eg prefixed with a full base_url or just a path component), or via relative URLs (possibly with a <base> tag in the HTML). Dynamic apps often allow the frontend host to pass the "path" as part of the request (eg PATH_INFO in FastCGI), or otherwise applications might statically prefix all the resources on a page and it is up to the user to configure this correctly in the app's settings. I'm only generally familiar with Home Assistant's internals but given the http base_url setting setting exists already, it appears to be following the latter "static" style of configuration.

To implement the ability for Home Assistant to be served with a path, the simplest solution appears to be a case of ensuring base_url affects Home Assistant's URLs correctly, where any assumptions have been made in the code about the app being hosted at the root (/) of the domain. For example, resources and links in the frontend HTML currently point at /frontend_latest and /static, so these would need the ability to be prefixed based on live configuration (or made relative). A comment on the previous issue mentioned there are other components that rely on knowing the path exposed to the internet so these would need adjusting too to make them flexible (& anywhere else that assumes the same).

Is Home Assistant's architecture able to accommodate being served on a path / is this something that can be done? I'm new to the project so happy to contribute a PR but I'd need some initial guidance from the core team to understand the scope and of changes required.

balloob commented 5 years ago

This will involve a LOT of work to both the backend and the frontend. And it will add work to each new feature/integration added, too. Or let alone the idea that a user would want to change the hosted path and all the config that now needs to be updated.

I don't think it's worth the effort to get there nor the effort to maintain it.

davidjb commented 5 years ago

@balloob What you're saying appears to indicate that there are parts of Home Assistant that don't use or adhere to the base_url setting, is that right? If that's the case, maybe the issue at hand is more about consistent support for base_url rather than specifically adding support for a path in that URL.

Out of curiosity, the documentation indicates that base_url currently affects the URL scheme, hostname and port. So, if for instance one wanted to change their installation's public hostname or port, is base_url the only setting you'd need to modify or are there more config changes required?

balloob commented 5 years ago

The frontend really doesn't care what host is used, but it will put a / in front of all their requests to make sure path's don't impact it.

davidjb commented 5 years ago

Okay, good to know -- so in that case, if it's set to prefix requests with /, that could be presumably be adjusted in configuration?

balloob commented 5 years ago

Closing this. Subdomain is the way to go. It adds a lot of complexity.

max5962 commented 5 years ago

So many people wants it. Some don't => closed. But why ? Why "sub-domain is the way to go" ? With that configuration I have to pay a premium no-ip (because it doesn't accept multiple sub-domain)

michaelarnauts commented 5 years ago

@max5962 there are many other ways. You can also use a second no-ip and use a proxy like nginx in front to serve the right page.

balloob commented 5 years ago

@max5962 making our code more complex for a feature hardly anyone wants is not worth it. It hurts the project longterm and we're not doing it.

LostLakkris commented 5 years ago

https://community.home-assistant.io/t/configurable-webroot/516 https://github.com/home-assistant/home-assistant/issues/805

Not sure who hardly anyone is, but this looks like something that's been asked for and waited on since 2015. Alot of users seemed to assume the support was already there from the base_url variable and it's inline explanation not explicitly clarifying that you mean host:port. Usually leading first to confusion on why it doesn't work, then conceding to subdomain or users finding the tickets where it was already requested.

I think the perception of "hardly anyone wants" is a bit inaccurate in the sense that many people probably want it, but don't believe they have any value to contribute to the conversation beyond :+1: . So they don't make themselves noticed. Pretty sure adding this feature will widen your user base.

I have a collection of users that the subdomain approach isn't an option for. It exposes that home-assistant is present in their homes which can make them targets from a security perspective. They prefer to use a hosted bastion-style nginx proxy, poking around it with invalid credentials doesn't expose anything, but probing for [sub]domains or checking configured DNS records gives away this service exists.

The lack of this feature put me off by about 2 years from finally putting this together in my own home, "I'll check back in 6 months and get started then when it handles custom URI roots" was what I kept telling myself.

Currently these users are restricted to VPN'ing to their homes, or communicating with their environments via Discord or similar from their phones.

That said, this is your project and your decision, but I wanted to at least toss in my usecases and related limitations for consideration. If you ever change your mind, I'd look forward to seeing this supported someday. If not, I think it's a pity.

zeehio commented 5 years ago

Maybe I am missing something obvious here... Shouldn't it be possible to achieve what you want with nginx, using a rewrite+reverse proxy setup?

https://stackoverflow.com/a/43168915/446149

davidjb commented 5 years ago

@zeehio That type of configuration rewrites requests to the application but doesn't affect the URLs and links the backend is generating (or that are hard-coded in places). For example, the frontend HTML is currently using hard-coded paths to point at resources served from /frontend_latest and /static; essentially it is making the assumption the serving is taking place at the root (/) of the domain with no virtual hosting possible. Certain components themselves (see above) are also making the same assumption about paths when generating URLs.

@LostLakkris You've summed up my setup in a nutshell. I missed seeing https://github.com/home-assistant/home-assistant/issues/805 in my original search, curious.

At any rate, with the base_url's protocol/hostname/port already being used for generating backend URLs, seemingly adding the ability to append a path in that mix would be possible. As mentioned, I'm new to the code base by happy to contribute PR(s). The frontend is possibly a little more complicated since the paths are hardcoded care of the webpack config, but I'm sure there'd be a way through that (eg make things relative).

balloob commented 5 years ago

Home Assistant will not accept a contribution that adds support for this. It's not worth the added complexity that we will have to deal with forever. This decision is final.

c608345 commented 5 years ago

front end change is not complex. In homepage change <link rel='preload' href='/frontend_es5/core.99aeb776.js' as='script'/> to <link rel='preload' href='./frontend_es5/core.99aeb776.js' as='script'/>
Add a "." in the path will make the url works.

dmak commented 5 years ago

I would certainly vote for this feature as having a subdomain requires a dedicated SSL certificate for it...

Shulyaka commented 5 years ago

May I suggest a workaround please? A documented list of paths needed to be redirected would solve the issue for many users. I.e. if I know exactly that I need to use reverse proxy for frontend_latest/, /static/ and /api/ it would still be the way to go, we just have to be sure that we don't miss, say, /onboarding.html or any other rare paths. A one step further would be to chose static, hardcoded, but less conflicting paths. For example, rename /api/ to /ha_api/ or even /homeassistant/api/. Or even put everything inside /homeassistant/ (hardcoded). That would not make it any more complex, wouldn't put any new strict requirements over new integrations, and wouldn't harm the project long term. At the same time it would allow a stable reverse proxy (and dual stack!) setups, which would make a lot of people even more happy.

Shulyaka commented 5 years ago

For instance, here is my config for lighttpd (/etc/lighttpd/conf.d/home-assistant.conf):

$HTTP["url"] =~ "^/(static/icons/|static/fonts/|static/translations/|static/images/|static/panels/|static/polyfills|api/|auth/|frontend_latest/|frontend_es5/|local/|lovelace|map|config|developer-tools|history|logbook|profile|states|hassio|onboarding.html|service_worker.js|authorize.html|manifest.json)" {
        proxy.server = ( "" => ( ( "host" => "127.0.0.1", "port" => "8123" ) ) )
        proxy.header = ( "upgrade" => "enable" )
        server.stream-request-body  = 2
        server.stream-response-body = 2
}

$HTTP["querystring"] =~ "(auth_callback=|homescreen=|external_auth=)" {
        proxy.server = ( "" => ( ( "host" => "127.0.0.1", "port" => "8123" ) ) )
        proxy.header = ( "upgrade" => "enable" )
        server.stream-request-body  = 2
        server.stream-response-body = 2
}

It is working, but you've probably got the idea why it is not optimal. Also, I need to update it sometimes (e.g. when /dev-event becomes /developer-tools/event).

rix1337 commented 4 years ago

Home Assistant will not accept a contribution that adds support for this. It's not worth the added complexity that we will have to deal with forever. This decision is final.

Sounds like we need a fork :)

HakunaMatmata commented 4 years ago

+1 Shame that this need is swept like that.. Remins me of CouchPotato, Sickbeard etc: pionneers in their domain but refused to evolve, then kicked out by new products much more flexible.

I've been looking for a solution all day, can't afford another SSL certificate, I guess I'll wait for the next product that will be flexible enough. A solution would be to avoid hardcoded URLs, not difficult to maintain and cleaner...

rix1337 commented 4 years ago

Yeah. I generally agree with the focus and complexity argument. My own projects would suffer as well.

Hardcoded urls however are just plane bugs. This is the last of 17 apps I host, that does not support path prefix. Even bitwarden_rs agreed and swiftly fixed this. Hard to imagine having to set up 17 specific domains to manage my server.

I understand you not wanting to spend time to do it yourselves. That's what we are here for. But just blocking any potential support ist wrong. This can and should be fixed and you should be open for a PR.

Shulyaka commented 4 years ago

Sounds like we need a fork :)

You probably don't understand how much effort it costs to maintain a fork. It's ridiculously more than maintaining a config file like this: https://github.com/home-assistant/architecture/issues/156#issuecomment-537670172

frenck commented 4 years ago

This discussion has been closed.

Please use another location for discussing this more socially. For example on Discord or our community forum. Thanks! 👍