Skip to content

Commit

Permalink
cockpit.remote: add init-superuser functionality
Browse files Browse the repository at this point in the history
When connecting to a remote host, consult the 'init-superuser' field of
the open message to decide what to request from the remote.  Implement
the 'authorize' and 'superuser-init-done' message handlers to pass along
the password we used for logging in.

Thanks to Martin and Marius for integration tests.

Fixes #18712

Co-authored-by: Marius Vollmer <[email protected]>
Co-authored-by: Martin Pitt <[email protected]>

Cherry-picked from main commit 0c88d78 to fix the last
pybridge regression covered by our tests.
  • Loading branch information
allisonkarlitskaya committed Sep 26, 2023
1 parent 9d1828c commit 9d36f33
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 11 deletions.
21 changes: 19 additions & 2 deletions src/cockpit/remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

from cockpit._vendor import ferny

from .jsonutil import JsonObject, get_str
from .jsonutil import JsonDocument, JsonObject, get_str, get_str_or_none
from .peer import Peer, PeerError
from .router import Router, RoutingRule

Expand Down Expand Up @@ -153,6 +153,15 @@ def do_kill(self, host: Optional[str], group: Optional[str]) -> None:
elif host is None:
super().do_kill(None, group)

def do_authorize(self, message: JsonObject) -> None:
if get_str(message, 'challenge').startswith('plain1:'):
cookie = get_str(message, 'cookie')
self.write_control(command='authorize', cookie=cookie, response=self.password or '')
self.password = None # once is enough...

def do_superuser_init_done(self) -> None:
self.password = None

def __init__(self, router: Router, host: str, user: Optional[str], options: JsonObject, *, private: bool) -> None:
super().__init__(router)
self.host = host
Expand All @@ -161,7 +170,15 @@ def __init__(self, router: Router, host: str, user: Optional[str], options: Json
self.private = private

self.session = ferny.Session()
self.start_in_background(init_host=host)

superuser: JsonDocument
init_superuser = get_str_or_none(options, 'init-superuser', None)
if init_superuser in (None, 'none'):
superuser = False
else:
superuser = {'id': init_superuser}

self.start_in_background(init_host=host, superuser=superuser)


class HostRoutingRule(RoutingRule):
Expand Down
49 changes: 41 additions & 8 deletions test/verify/check-shell-multi-os
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def add_stock_machine(b, address, password):


@testlib.skipDistroPackage()
class TestMultiOS(testlib.MachineCase):
class TestCentos7(testlib.MachineCase):
provision = {
"0": {"address": "10.111.113.1/20", "memory_mb": 512},
"centos-7": {"address": "10.111.113.5/20", "image": "centos-7", "memory_mb": 512}
Expand All @@ -95,16 +95,10 @@ class TestMultiOS(testlib.MachineCase):
})""", address)

@testlib.todoPybridgeRHEL8()
def testCentOS7(self):
def test(self):
dev_m = self.machine
dev_b = self.browser

# Newer bridges running under an old cockpit-ws don't get the
# support that they need to prompt for the password. So open
# up sudo for this test.
dev_m.execute("echo '%wheel ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers.d/user-override")
dev_m.execute("echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers.d/user-override")

self.allow_hostkey_messages()

self.login_and_go()
Expand Down Expand Up @@ -160,6 +154,45 @@ class TestMultiOS(testlib.MachineCase):
self.allow_restart_journal_messages()


@testlib.skipDistroPackage()
class TestRHEL8(testlib.MachineCase):
provision = {
"0": {"address": "10.111.113.1/20", "memory_mb": 768},
"stock": {"address": "10.111.113.5/20", "image": "rhel-8-8", "memory_mb": 512}
}

@testlib.todoPybridgeRHEL8()
def test(self):
dev_m = self.machine
dev_b = self.browser

stock_m = self.machines['stock']
stock_m.execute("hostnamectl set-hostname stock")

# Wait for connectivity between the two
stock_m.execute("ping -q -w5 -c5 10.111.113.1")
dev_m.execute("ping -q -w5 -c5 10.111.113.5")

self.allow_hostkey_messages()

# New machine adding old machine

self.login_and_go()
dev_b.click("#hosts-sel button")
dev_addresses = ["localhost"]
wait_addresses(dev_b, dev_addresses)

add_machine(dev_b, "10.111.113.5", password="foobar")
dev_addresses.append("10.111.113.5")
wait_addresses(dev_b, dev_addresses)

dev_b.go("/@10.111.113.5/")
dev_b.wait_visible("iframe.container-frame[name='cockpit1:10.111.113.5/system'][data-loaded]")
dev_b.enter_page("/system", host="10.111.113.5")
dev_b.wait_in_text(".ct-overview-header-hostname", "stock")
dev_b.wait_in_text(".ct-overview-header-hostname", "running Red Hat Enterprise Linux 8")


@testlib.skipDistroPackage()
class TestMultiOSDirect(testlib.MachineCase):
provision = {
Expand Down
18 changes: 17 additions & 1 deletion test/verify/check-superuser
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,22 @@ class TestSuperuserDashboard(testlib.MachineCase):
b.wait_in_text(".super-channel span", 'result: ')
self.assertIn('result: uid=0', b.text(".super-channel span"))

# Logging out and logging back in should give us immediate
# superuser on m2 (once we have logged in there).
b.relogin()
b.wait_visible("#machine-troubleshoot")
b.click('#machine-troubleshoot')
b.wait_visible('#hosts_setup_server_dialog')
b.set_input_text("#login-custom-password", "foobar")
b.click('#hosts_setup_server_dialog button:contains("Log in")')
b.wait_not_present('#hosts_setup_server_dialog')
b.check_superuser_indicator("Administrative access")

b.enter_page("/playground/test", host="10.111.113.2")
b.click(".super-channel button")
b.wait_in_text(".super-channel span", 'result: ')
self.assertIn('result: uid=0', b.text(".super-channel span"))

b.drop_superuser()
b.click(".super-channel button")
b.wait_in_text(".super-channel span", 'access-denied')
Expand Down Expand Up @@ -705,7 +721,7 @@ class TestSuperuserDashboardOldMachine(testlib.MachineCase):
"machine2": {"address": "10.111.113.2/20", "image": "centos-7"},
}

@testlib.todoPybridge(reason="https://github.com/cockpit-project/cockpit/issues/18712")
@testlib.todoPybridgeRHEL8()
def test(self):
b = self.browser

Expand Down

0 comments on commit 9d36f33

Please sign in to comment.