We have this pending pull request (https://github.com/cockpit-project/cockpit/pull/6260) that adds sandboxing to our iframes. This stops child iframes from being able to change stuff in the parent. However it doesn't actually stop html and js loaded from one server from being able to access stuff in another server. This probably becomes more important once we start allowing cockpit to load packages from containers, we wouldn't want those packages to be able to break out and access the host.
When channel requests are routed through the shell we can of course enforce that the "host" param matches the host that the html is loaded from. However that doesn't prevent one machine from establishing it's own websocket connection and running whatever it wanted on any other host.
So my proposal is that in cockpit-ws we remove "ws: wss:" from the CSP policy for any html page served from a machine other than localhost. This forbids any html page served from a different host from making a websocket connection. This has a few implications.
Only packages served from localhost will ever be allowed to access a different machine (or container) than what it was served from. In some ways this matches what we already have. Where we only serve dashboards from localhost. However, this means that for example we couldn't serve the dashboard application out of a container. It wants to connect to different hosts than the one it is served from and that would be forbidden. Although it may be possible to have some sort of whitelisted setting in the machines.json that would allow something like this.
We can never have localhost and another machine have the same checksum even if packages between them are exactly the same.
We won't allow embedding of applications from secondary machines without going through the shell. Note that this currently seems to work but actually doesn't. For example loading http://localhost:9090/cockpit/@machine1/system/index.html#/ directly will load the html from machine1. But cockpit.js will still have default host as localhost and open channels for localhost instead of machine1. Exactly what we want to avoid.
That leaves external channels. Best idea I came up with is to have a csrf token per host. The csrf token would need to match the 'host' value from the open message before allowing it to proceed. These tokens would need to be tracked in cockpitwebservice and we'd let the shell know about them with socket hints.