Skip to content

Commit

Permalink
ws: Conditionally block channel requests to remote hosts
Browse files Browse the repository at this point in the history
When AllowMultiHost is false, cockpit-ws will reject all GET requests
that would load from a non-localhost bridge.
  • Loading branch information
mvollmer committed Sep 17, 2024
1 parent 6592eda commit 75c332c
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 0 deletions.
9 changes: 9 additions & 0 deletions src/ws/cockpitchannelresponse.c
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,7 @@ cockpit_channel_response_serve (CockpitWebService *service,
gchar *channel = NULL;
gpointer key;
gpointer value;
gboolean allow_multihost;

g_return_if_fail (COCKPIT_IS_WEB_SERVICE (service));
g_return_if_fail (in_headers != NULL);
Expand All @@ -614,6 +615,14 @@ cockpit_channel_response_serve (CockpitWebService *service,
goto out;
}

allow_multihost = cockpit_conf_bool ("WebService", "AllowMultiHost", ALLOW_MULTIHOST_DEFAULT);
if (!allow_multihost && g_strcmp0 (host, "localhost") != 0)
{
cockpit_web_response_error (response, 403, NULL, NULL);
handled = TRUE;
goto out;
}

if (quoted_etag)
{
cache_type = COCKPIT_WEB_RESPONSE_CACHE;
Expand Down
48 changes: 48 additions & 0 deletions test/verify/check-connection
Original file line number Diff line number Diff line change
Expand Up @@ -1443,5 +1443,53 @@ server {
self.allow_journal_messages("couldn't change to runtime dir.*Permission denied")


class TestConnectionMultiHost(testlib.MachineCase):
provision = {
'm1': {"address": "10.111.113.1/20", "memory_mb": 512},
'm2': {"address": "10.111.113.2/20", "memory_mb": 512},
}

def testBasic(self):
b = self.browser
m1 = self.machines['m1']
m2 = self.machines['m2']

self.enable_multihost(m1)

# setup seamless ssh auth from machine1 to machine2
self.setup_ssh_auth()

# login into cockpit on machine1
self.login_and_go("/system")
cookie = b.cookie("cockpit")

def http_code(url):
return int(subprocess.check_output(["curl",
"--silent",
"-b", f"cockpit={cookie['value']}",
"-o", "/dev/null", "-w", "%{http_code}",
f"http://{b.address}:{b.port}{url}"]))

# Now we can get resources from m1
self.assertEqual(200, http_code("/cockpit/@localhost/manifests.json"))

# But not from m2
self.assertEqual(502, http_code("/cockpit/@10.111.113.2/manifests.json"))

# Unless we have stored the hostkey of m2
hk = m2.execute("cat /etc/ssh/ssh_host_ecdsa_key.pub").strip()
m1.execute(f"f=/home/admin/.ssh/known_hosts; echo 10.111.113.2 {hk} >$f; chown admin:admin $f")
self.assertEqual(200, http_code("/cockpit/@10.111.113.2/manifests.json"))

# But not when AllowMultiHost is false
m1.write("/etc/cockpit/cockpit.conf",
'[WebService]\nAllowMultiHost=no\n')
m1.restart_cockpit()
b.relogin("/system")
cookie = b.cookie("cockpit")

self.assertEqual(200, http_code("/cockpit/@localhost/manifests.json"))
self.assertEqual(403, http_code("/cockpit/@10.111.113.2/manifests.json"))

if __name__ == '__main__':
testlib.test_main()

0 comments on commit 75c332c

Please sign in to comment.