Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

containers/ws: Support using an external SSH agent #21202

Merged
merged 1 commit into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion containers/ws/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ this by passing your own configuration as a volume:
Similarly you can also provide a custom `/etc/os-release` to change the
branding.

### SSH authentication
### SSH authentication: Share keys with container

The login page asks the user to confirm unknown SSH host key fingerprints. You
can mount your known host keys into the container at
Expand All @@ -119,6 +119,20 @@ You can also mount encrypted private keys inside the container. You can set an e

Private keys can be encrypted; then cockpit uses the provided password to decrypt the key.

### SSH authentication: Share SSH agent with container

Alternatively, if you use [ssh-agent](https://linux.die.net/man/1/ssh-agent) on
your host, you can share it with the container and run the container as your
own user (*not* as system container!). Then logging into remote machines from
Cockpit's login page re-uses the loaded private keys. For that, bind-mount the
agent socket in the container and tell it its path. If your host has SELinux
enabled, you need to disable the isolation for the container, so that it is
allowed to connect to the agent socket:

-v $SSH_AUTH_SOCK:/ssh-agent \
-e SSH_AUTH_SOCK=/ssh-agent \
--security-opt=label=disable

## More Info

* [Cockpit Project](https://cockpit-project.org)
Expand Down
4 changes: 3 additions & 1 deletion containers/ws/label-run
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ else

/usr/libexec/cockpit-certificate-ensure

eval $(ssh-agent)
# start SSH agent, unless we already got pointed to one
[ -n "${SSH_AUTH_SOCK:-}" ] || eval "$(ssh-agent)"

exec /usr/libexec/cockpit-ws --local-ssh "$@"
fi
52 changes: 52 additions & 0 deletions test/verify/check-ws-bastion
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,58 @@ class TestWsBastionContainer(testlib.MachineCase):
for ssh_key_env in ["COCKPIT_SSH_KEY_PATH", f"COCKPIT_SSH_KEY_PATH_{HOST.upper()}"]:
do_test_key_login(ssh_key_env=ssh_key_env)

def testExternalAgent(self):
m = self.machine
b = self.browser

KEY_PASSWORD = "sshfoobar"

# run the container as user -- as root does not make sense, as we want to
# share the user's SSH key with it
m.execute(f"podman save localhost/cockpit/ws -o {self.vm_tmpdir}/cockpit-ws.tar")

# HACK: user podman does not add default route if the host doesn't have any
m.execute("ip route add default via 172.27.0.1")
self.addCleanup(m.execute, "ip route del default")

self.restore_dir("/home/admin")
m.execute("runuser -u admin -- sh -ex", input=f"""
# we don't start a real login session here, fake it
cd $HOME
export XDG_RUNTIME_DIR=$HOME/run
mkdir -p "$XDG_RUNTIME_DIR"

# create new key, set it up for logging into host
ssh-keygen -q -f ~/.ssh/id_bastion -N {KEY_PASSWORD}
cat ~/.ssh/id_bastion.pub > /home/admin/.ssh/authorized_keys
ssh-keyscan localhost | sed 's/^localhost/{HOST}/' > ~/.ssh/known_hosts

# start agent, load key
eval $(ssh-agent -a $XDG_RUNTIME_DIR/ssh-agent)
printf '#!/bin/sh\necho {KEY_PASSWORD}\n' > /tmp/pwd
chmod u+x /tmp/pwd
SSH_ASKPASS_REQUIRE=force SSH_ASKPASS=/tmp/pwd ssh-add ~/.ssh/id_bastion
rm /tmp/pwd
ssh-add -l

# run container
podman load -i {self.vm_tmpdir}/cockpit-ws.tar
podman run -d --rm --name cockpit-bastion -p 9090:9090 \
-v ~/.ssh/known_hosts:/etc/ssh/ssh_known_hosts:ro \
-v $XDG_RUNTIME_DIR/ssh-agent:/ssh-agent \
-e SSH_AUTH_SOCK=/ssh-agent \
--security-opt=label=disable \
-e COCKPIT_DEBUG=all localhost/cockpit/ws

until curl --fail --head -k https://localhost:9090/; do sleep 1; done
""")

# login works without a password, using the external agent
b.open("/", tls=True)
b.set_val("#server-field", HOST)
b.try_login(password="")
b.wait_visible('#content')


@testlib.onlyImage("no cockpit/ws container on this image", "fedora-coreos")
@testlib.nondestructive
Expand Down
Loading