Skip to content

Commit

Permalink
containers/ws: Enable beibooting and install standard cockpit pages
Browse files Browse the repository at this point in the history
This provides similar functionality as the Client, so that the ws
bastion container can connect to machines which don't have any Cockpit
packages installed.

Install cockpit-system and cockpit-networkmanager into the container
(we'll likely expand this in the future, but let's start small). Take
care to only unpack the RPMs, to avoid installing e.g. NetworkManager
and other big dependencies into the container -- they are useless there.

Update the documentation accordingly. While at it, add an introduction
paragraph what Cockpit is.

https://issues.redhat.com/browse/COCKPIT-954
  • Loading branch information
martinpitt committed Oct 1, 2024
1 parent dc2085e commit ab36148
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 28 deletions.
42 changes: 27 additions & 15 deletions containers/ws/README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,37 @@
# Cockpit on Fedora CoreOS or other container hosts
# Cockpit webserver container

The standard Fedora and Red Hat Enterprise Linux CoreOS images does not contain
Cockpit packages.
[Cockpit](https://cockpit-project.org/) is a web-based graphical interface for Linux servers.
It is [packaged in most major Linux distributions](https://cockpit-project.org/running.html).

1. Install Cockpit packages as overlay RPMs:
```
rpm-ostree install cockpit-system cockpit-ostree cockpit-podman
```
This container image provides Cockpit's web server and a subset of available pages (like the cockpit-system package)
for deployment on container hosts such as Fedora CoreOS or Kubernetes, where installing rpms is difficult or impossible.

## Usage on container host distributions

The standard Fedora and Red Hat Enterprise Linux CoreOS images do not contain Cockpit packages. The
`cockpit/ws` container includes a minimal set of builtin Cockpit pages which are being used when connecting to
such a machine, i.e. a host which doesn't have the `cockpit-bridge` package installed.

If these builtin pages are not enough for your use cases, you can install desired Cockpit packages
as overlay RPMs. For example:

```
rpm-ostree install cockpit-system cockpit-ostree cockpit-podman
reboot
```

Depending on your configuration, you may want to use
[other extensions](https://apps.fedoraproject.org/packages/s/cockpit-) as
well, such as `cockpit-kdump` or `cockpit-networkmanager`.
Depending on your configuration, you may want to use
[other extensions](https://packages.fedoraproject.org/search?query=cockpit-) as
well, such as `cockpit-podman` or `cockpit-networkmanager`.

If you have a custom-built OSTree, simply include the same packages in your build.
If you have a custom-built OSTree, simply include the same packages in your build.

2. Reboot
These packages are enough when the CoreOS machine is only connected to through another host running Cockpit.

Steps 1 and 2 are enough when the CoreOS machine is only connected to through another host running Cockpit.
You also need to run a Cockpit web server somewhere, as the "entry point" for browsers. That can
then connect to the local host or any remote machine via ssh to get a Cockpit UI for that machine.

If you want to also run a web server to log in directly on the CoreOS host, you
can use this container in two modes.
This web server can be deployed as container. It has two modes, which are described below.

## Privileged ws container

Expand Down
4 changes: 1 addition & 3 deletions containers/ws/cockpit-auth-ssh-key
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,7 @@ def main(args):
{"password": "denied"})
return

# for the time being, we only support running an installed cockpit-bridge on the remote,
# and leave beibooting to the flatpak
os.execlpe("python3", "python3", "-m", "cockpit.beiboot", "--remote-bridge=always", host, os.environ)
os.execlpe("python3", "python3", "-m", "cockpit.beiboot", host, os.environ)


if __name__ == '__main__':
Expand Down
17 changes: 16 additions & 1 deletion containers/ws/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,33 @@
set -ex

OSVER=$(. /etc/os-release && echo "$VERSION_ID")
INSTALLROOT=/build
INSTALL="dnf install -y --installroot=$INSTALLROOT --releasever=$OSVER --setopt=install_weak_deps=False"

INSTALL="dnf install -y --installroot=/build --releasever=$OSVER --setopt=install_weak_deps=False"
dnf install -y 'dnf-command(download)' cpio
$INSTALL coreutils-single util-linux-core sed sscg python3 openssh-clients

arch=`uname -p`
rpm=$(ls /container/rpms/cockpit-ws-*$OSVER.*$arch.rpm /container/rpms/cockpit-bridge-*$OSVER.*$arch.rpm || true)

unpack() {
rpm2cpio "$1" | cpio -i --make-directories --directory=$INSTALLROOT
}

# If there are rpm files in the current directory we'll install those
# -system and -networkmanager are only for beibooting; don't install their dependencies
if [ -n "$rpm" ]; then
$INSTALL /container/rpms/cockpit-ws-*$OSVER.*$arch.rpm /container/rpms/cockpit-bridge-*$OSVER.*$arch.rpm
for rpm in /container/rpms/cockpit-system-*$OSVER.*$arch.rpm \
/container/rpms/cockpit-networkmanager-*$OSVER.*$arch.rpm; do
unpack $rpm
done
else
$INSTALL cockpit-ws cockpit-bridge
dnf download cockpit-networkmanager cockpit-system
for rpm in cockpit-networkmanager*.rpm cockpit-system*.rpm; do
unpack $rpm
done
fi

rm -rf /build/var/cache/dnf /build/var/lib/dnf /build/var/lib/rpm* /build/var/log/*
Expand Down
12 changes: 8 additions & 4 deletions test/ostree.install
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
#!/bin/sh
set -eu

# install/upgrade RPMs that apply to OSTree
# Note: cockpit-selinux would be desirable, but needs setroubleshoot-server which isn't installed
cd /var/tmp/

# install/upgrade RPMs that apply to OSTree
# Note: cockpit-selinux would be desirable, but needs setroubleshoot-server which isn't installed
rpm-ostree install --cache-only cockpit-bridge-*.rpm \
cockpit-networkmanager-*.rpm cockpit-system-*.rpm cockpit-tests-*.rpm

# update cockpit-ws and install scripts in the container
for rpm in /var/tmp/cockpit-ws-*.rpm /var/tmp/cockpit-bridge-*.rpm; do
# update cockpit packages and install scripts in the container
for rpm in /var/tmp/cockpit-ws-*.rpm \
/var/tmp/cockpit-bridge-*.rpm \
/var/tmp/cockpit-networkmanager-*.rpm \
/var/tmp/cockpit-system-*.rpm \
/var/tmp/cockpit-tests-*.rpm; do
rpm2cpio "$rpm" | cpio -i --make-directories --directory=/var/tmp/install
done
podman run --name build-cockpit -i \
Expand Down
28 changes: 23 additions & 5 deletions test/verify/check-ws-bastion
Original file line number Diff line number Diff line change
Expand Up @@ -70,19 +70,25 @@ class TestWsBastionContainer(testlib.MachineCase):

b.wait_visible('#content')
b.wait_text('#current-username', 'admin')
# runs the ssh target's bridge (no beiboot)
m.execute("pgrep -f /usr/bin/[c]ockpit-bridge")
b.logout()

# remembers the last host via URL, server field should be pre-filled
self.assertEqual(b.eval_js("window.location.pathname"), f"/={HOST}/system")
# FIXME: login page does not really set this in the DOM? DOM has empty value, but browser shows the value
# b.wait_text("#server-field", host)
# this is only for Cockpit Client
b.wait_not_visible("#recent-hosts")
b.set_val("#login-user-input", "admin")
b.set_val("#login-password-input", "foobar")
# second time SSH key is known
b.click("#login-button")
b.try_login()
b.wait_visible('#content')
b.logout()

# disable target bridge, should use beiboot
m.execute("mount -o bind /dev/null /usr/bin/cockpit-bridge")
self.addCleanup(m.execute, "umount /usr/bin/cockpit-bridge")
b.try_login()
b.wait_visible('#content')
m.execute("pgrep -f '[p]ython3 -ic # cockpit-bridge'")
b.logout()

def testKnownHosts(self):
Expand Down Expand Up @@ -203,6 +209,8 @@ class TestWsBastionContainer(testlib.MachineCase):
b.set_val("#login-password-input", KEY_PASSWORD)
b.click("#login-button")
b.wait_visible('#content')
# runs the ssh target's bridge (no beiboot)
m.execute("pgrep -f /usr/bin/[c]ockpit-bridge")
b.logout()

# now test with current OpenSSH format
Expand All @@ -213,8 +221,18 @@ class TestWsBastionContainer(testlib.MachineCase):
b.set_val("#login-password-input", KEY_PASSWORD)
b.click("#login-button")
b.wait_visible('#content')
b.logout()

# disable target bridge, should use beiboot
m.execute("mount -o bind /dev/null /usr/bin/cockpit-bridge")
try:
b.try_login(password=KEY_PASSWORD)
b.wait_visible('#content')
m.execute("pgrep -f '[p]ython3 -ic # cockpit-bridge'")
finally:
m.execute("umount /usr/bin/cockpit-bridge")
b.logout()

m.execute("podman rm -f -t0 cockpit-bastion")
m.execute("rm /home/admin/.ssh/authorized_keys")

Expand Down

0 comments on commit ab36148

Please sign in to comment.