From 57efefbaea05c048387fab77becdbd2ab7c336e9 Mon Sep 17 00:00:00 2001 From: Marius Vollmer Date: Wed, 31 Jul 2024 16:22:27 +0300 Subject: [PATCH 1/2] shell: Remove the "half-off" mode of the host switcher Cockpit should either not connect to remote machines at all, or trust the user to do it right. Also, the better place to explain the security implications of connecting to remote hosts is when actually connecting. --- pkg/shell/hosts.jsx | 22 +--------------------- pkg/shell/indexes.jsx | 3 +-- test/verify/check-shell-host-switching | 16 ---------------- 3 files changed, 2 insertions(+), 39 deletions(-) diff --git a/pkg/shell/hosts.jsx b/pkg/shell/hosts.jsx index 361ae0b19a92..d8b23cb788b5 100644 --- a/pkg/shell/hosts.jsx +++ b/pkg/shell/hosts.jsx @@ -8,13 +8,9 @@ import { CaretDownIcon, CaretUpIcon, EditIcon, - ExclamationCircleIcon, - ExternalLinkAltIcon, MinusIcon, } from '@patternfly/react-icons'; -import { Label } from "@patternfly/react-core/dist/esm/components/Label"; import { PageSidebar } from "@patternfly/react-core/dist/esm/components/Page"; -import { Popover } from "@patternfly/react-core/dist/esm/components/Popover"; import { Tooltip } from "@patternfly/react-core/dist/esm/components/Tooltip"; import 'polyfills'; @@ -197,22 +193,7 @@ export class CockpitHosts extends React.Component { const label = this.props.machine.label || ""; const user = this.props.machine.user || this.state.current_user; - let add_host_action; - - if (this.props.enable_add_host) { - add_host_action = ; - } else { - const footer = - {_("Read more...")} - ; - add_host_action = ( - - - ); - } + const add_host_action = ; return ( <> @@ -285,5 +266,4 @@ CockpitHosts.propTypes = { selector: PropTypes.string.isRequired, hostAddr: PropTypes.func.isRequired, jump: PropTypes.func.isRequired, - enable_add_host: PropTypes.bool.isRequired, }; diff --git a/pkg/shell/indexes.jsx b/pkg/shell/indexes.jsx index c13755c5bd58..7833c9b695bf 100644 --- a/pkg/shell/indexes.jsx +++ b/pkg/shell/indexes.jsx @@ -381,7 +381,7 @@ function MachinesIndex(index_options, machines, loader) { machine = machines.lookup(state.host); // deprecation transition period: show existing remote hosts, but disable adding new ones - if (host_switcher_enabled || machines.list.length > 1) { + if (host_switcher_enabled) { hosts_sel_root.render( React.createElement(CockpitHosts, { machine: machine || {}, @@ -389,7 +389,6 @@ function MachinesIndex(index_options, machines, loader) { selector: "nav-hosts", hostAddr: index.href, jump: index.jump, - enable_add_host: host_switcher_enabled, })); } else { hosts_sel_root.render(React.createElement(CockpitCurrentHost, { machine: machine || {} })); diff --git a/test/verify/check-shell-host-switching b/test/verify/check-shell-host-switching index 9e07cd16415f..b4a10244b391 100755 --- a/test/verify/check-shell-host-switching +++ b/test/verify/check-shell-host-switching @@ -134,22 +134,6 @@ class TestHostSwitching(testlib.MachineCase, HostSwitcherHelpers): b.assert_pixels("#hosts-sel", "no-switching", skip_layouts=["mobile"]) b.logout() - # transition period: Existing remote hosts are still shown - m1.write("/etc/cockpit/machines.d/99-webui.json", - '{"srv": { "address": "my.srv", "visible": true }}') - b.login_and_go(superuser=False) - b.click("#hosts-sel button") - self.wait_host_addresses(b, ["localhost", "my.srv"]) - b.wait_in_text(".deprecated-add-host", "Deprecated") - b.assert_pixels(".nav-hosts-actions", "deprecated", skip_layouts=["mobile"]) - # clicking on it shows popover with help - b.click(".deprecated-add-host") - b.wait_text("#popover-disabled-add-host-help-header", "Host switching is not supported") - b.click(".pf-v5-c-popover__close button") - b.wait_not_present("#popover-disabled-add-host-help-header") - b.logout() - m1.execute("rm /etc/cockpit/machines.d/99-webui.json") - # enable host switcher for this test self.enable_multihost(m1) b.login_and_go(superuser=False) From b2c92d6cd252c79fcb4617810c899b556d4f15c6 Mon Sep 17 00:00:00 2001 From: Marius Vollmer Date: Tue, 6 Aug 2024 15:16:41 +0300 Subject: [PATCH 2/2] shell: Don't allow connections to remote machines via URLs... ...when the host switcher is disabled. Instead, redirect them to localhost. The tests that use multiple machines add those machines by navigating to their URL and then logging into them via the trouble shooting dialog. Those tests have to explicitly enable the host switcher for this to continue to work. --- pkg/shell/indexes.jsx | 8 ++++++ test/reference | 2 +- test/verify/check-shell-host-switching | 31 ++++++++++++++++------- test/verify/check-shell-multi-machine | 4 ++- test/verify/check-shell-multi-os | 1 + test/verify/check-superuser | 1 + test/verify/check-system-realms | 10 +++++++- test/verify/check-system-shutdown-restart | 2 ++ 8 files changed, 47 insertions(+), 12 deletions(-) diff --git a/pkg/shell/indexes.jsx b/pkg/shell/indexes.jsx index 7833c9b695bf..e8d6fabb6ca7 100644 --- a/pkg/shell/indexes.jsx +++ b/pkg/shell/indexes.jsx @@ -179,6 +179,14 @@ function MachinesIndex(index_options, machines, loader) { if (!state) state = index.retrieve_state(); + + // Force a redirect to localhost when the host switcher is + // disabled. That way, people won't accidentally connect to + // remote machines via URL bookmarks or similar that point to + // them. + if (!host_switcher_enabled) + state.host = "localhost"; + let machine = machines.lookup(state.host); /* No such machine */ diff --git a/test/reference b/test/reference index 48dcaa2bd66b..49ef2e9d7c49 160000 --- a/test/reference +++ b/test/reference @@ -1 +1 @@ -Subproject commit 48dcaa2bd66bf7efaf35301916bef3adeadfe61c +Subproject commit 49ef2e9d7c491157f66abe28c32907a96c6844b9 diff --git a/test/verify/check-shell-host-switching b/test/verify/check-shell-host-switching index b4a10244b391..5c99ad4d79e5 100755 --- a/test/verify/check-shell-host-switching +++ b/test/verify/check-shell-host-switching @@ -124,19 +124,32 @@ class TestHostSwitching(testlib.MachineCase, HostSwitcherHelpers): m2.execute("hostnamectl set-hostname machine2") m3.execute("hostnamectl set-hostname machine3") + # Switch the switcher off for the begining of this test. + if self.multihost_enabled: + m1.write("/etc/cockpit/cockpit.conf", + '[WebService]\nAllowMultiHost=no\n') + # This should all work without being admin on machine1 self.login_and_go(superuser=False) - # on recent OSes, switcher is disabled by default - if not self.multihost_enabled: - b.wait_text("#hosts-sel .ct-switcher-localonly", "admin@localhost") - self.assertFalse(b.is_present("#hosts-sel button")) - b.assert_pixels("#hosts-sel", "no-switching", skip_layouts=["mobile"]) - b.logout() + b.wait_text("#hosts-sel .ct-switcher-localonly", "admin@localhost") + self.assertFalse(b.is_present("#hosts-sel button")) + b.assert_pixels("#hosts-sel", "no-switching", skip_layouts=["mobile"]) - # enable host switcher for this test - self.enable_multihost(m1) - b.login_and_go(superuser=False) + # Check that URLs to remore hosts get redirected to the local + # session. + b.wait_js_cond('window.location.pathname == "/system"') + b.go("/@10.111.113.2/storage") + b.wait_js_cond('window.location.pathname == "/storage"') + + # Enable host switcher for the rest of the test + b.logout() + if self.multihost_enabled: + # clean up AllowMultiHost=no from above + m1.execute("rm /etc/cockpit/cockpit.conf") + self.enable_multihost(m1) + m1.restart_cockpit() + b.login_and_go(superuser=False) b.assert_pixels("#nav-system", "nav-system", skip_layouts=["mobile"]) b.set_layout("mobile") diff --git a/test/verify/check-shell-multi-machine b/test/verify/check-shell-multi-machine index f92fa6244c51..fe69d05e69f7 100755 --- a/test/verify/check-shell-multi-machine +++ b/test/verify/check-shell-multi-machine @@ -134,6 +134,7 @@ class TestMultiMachineAdd(testlib.MachineCase): # and failing to load sofware updates breaks pixel tests in release builds self.setup_provisioned_hosts(disable_preload=True) self.setup_ssh_auth() + self.enable_multihost(self.machine) def testBasic(self): b = self.browser @@ -250,6 +251,7 @@ class TestMultiMachine(testlib.MachineCase): self.allow_journal_messages("sudo: unable to resolve host machine1: .*") self.setup_provisioned_hosts(disable_preload=True) + self.enable_multihost(self.machine) def checkDirectLogin(self, root='/', known_host=False): b = self.browser @@ -381,7 +383,7 @@ class TestMultiMachine(testlib.MachineCase): hostname_selector = "#system_information_hostname_text" - m.write("/etc/cockpit/cockpit.conf", "[WebService]\nUrlRoot = cockpit-new") + m.write("/etc/cockpit/cockpit.conf", "[WebService]\nUrlRoot = cockpit-new\nAllowMultiHost=yes\n") m.start_cockpit() # Make sure normal urls don't work. diff --git a/test/verify/check-shell-multi-os b/test/verify/check-shell-multi-os index 3da6fc7da47a..06890cd2c750 100755 --- a/test/verify/check-shell-multi-os +++ b/test/verify/check-shell-multi-os @@ -33,6 +33,7 @@ class TestRHEL8(testlib.MachineCase): stock_m = self.machines['stock'] stock_m.execute("hostnamectl set-hostname stock") + self.enable_multihost(dev_m) # Wait for connectivity between the two stock_m.execute("ping -q -w5 -c5 10.111.113.1") diff --git a/test/verify/check-superuser b/test/verify/check-superuser index 933029c6a582..e5cfe7892caf 100755 --- a/test/verify/check-superuser +++ b/test/verify/check-superuser @@ -405,6 +405,7 @@ class TestSuperuserDashboard(testlib.MachineCase): def test(self): b = self.browser self.setup_provisioned_hosts() + self.enable_multihost(self.machine) self.login_and_go() b.go("/@10.111.113.2") diff --git a/test/verify/check-system-realms b/test/verify/check-system-realms index 151ff23f8052..3d7e4707f52e 100755 --- a/test/verify/check-system-realms +++ b/test/verify/check-system-realms @@ -437,7 +437,13 @@ class CommonTests: do_test(alice_user_pass, ['HTTP/1.1 200 OK', '"csrf-token"'], session_leader='cockpit-session', retry=True) # enable cert based auth - m.write("/etc/cockpit/cockpit.conf", '[WebService]\nClientCertAuthentication = true\n', append=True) + # FIXME: this is ugly, and we don't currently accept multiple [WebService] sections + if self.multihost_enabled: + # no previous cockpit.conf + m.write("/etc/cockpit/cockpit.conf", '[WebService]\nClientCertAuthentication = true\n', append=True) + else: + # enable_multihost above already wrote [WebService] + m.write("/etc/cockpit/cockpit.conf", 'ClientCertAuthentication = true\n', append=True) # cert auth should work now do_test(alice_cert_key, ['HTTP/1.1 200 OK', '"csrf-token"']) # password auth, too @@ -495,6 +501,7 @@ class TestRealms(testlib.MachineCase): self.op_admin_password = "#realms-op-admin-password" self.domain_sel = "#system_information_domain_button" self.machine.execute("hostnamectl set-hostname x0.cockpit.lan") + self.enable_multihost(self.machine) # realmd times out on inactivity, which occasionally races with the proxy self.allow_journal_messages("couldn't get all properties of org.freedesktop.realmd.Service.*org.freedesktop.DBus.Error.NoReply: Remote peer disconnected") @@ -989,6 +996,7 @@ class TestKerberos(testlib.MachineCase): def setUp(self): super().setUp() maybe_setup_fake_chrony(self.machine) + self.enable_multihost(self.machine) def configure_kerberos(self, keytab): self.machines["services"].execute("/root/run-freeipa") diff --git a/test/verify/check-system-shutdown-restart b/test/verify/check-system-shutdown-restart index 5d276162153a..8ac48dc3fb88 100755 --- a/test/verify/check-system-shutdown-restart +++ b/test/verify/check-system-shutdown-restart @@ -38,6 +38,8 @@ class TestShutdownRestart(testlib.MachineCase): m2 = self.machines['machine2'] b2 = self.new_browser(m2) + self.enable_multihost(m2) + m.start_cockpit() self.login_and_go("/system")