
On Fri, Apr 19, 2024 at 12:12:31PM +0200, Michal Privoznik wrote:
This allows users to SSH into a domain with a VSOCK device:
ssh user@qemu/machineName
So far, only QEMU domains are supported AND qemu:///system is looked for the first for 'machineName' followed by qemu:///session. I took an inspiration from SystemD's ssh proxy [1] [2].
To just work out of the box, it requires (yet unreleased) systemd to be running inside the guest to set up a socket activated SSHD on the VSOCK. Alternatively, users can set up the socket activation themselves, or just run a socat that'll forward vsock <-> TCP communication.
1: https://github.com/systemd/systemd/blob/main/src/ssh-generator/ssh-proxy.c 2: https://github.com/systemd/systemd/blob/main/src/ssh-generator/20-systemd-ss...
Resolves: https://gitlab.com/libvirt/libvirt/-/issues/579 Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- libvirt.spec.in | 27 +++ meson.build | 16 +- meson_options.txt | 2 + po/POTFILES | 1 + tools/meson.build | 2 + tools/ssh-proxy/30-libvirt-ssh-proxy.conf.in | 10 + tools/ssh-proxy/meson.build | 25 ++ tools/ssh-proxy/ssh-proxy.c | 233 +++++++++++++++++++ 8 files changed, 315 insertions(+), 1 deletion(-) create mode 100644 tools/ssh-proxy/30-libvirt-ssh-proxy.conf.in create mode 100644 tools/ssh-proxy/meson.build create mode 100644 tools/ssh-proxy/ssh-proxy.c
@@ -1017,6 +1018,9 @@ Summary: Client side utilities of the libvirt library Requires: libvirt-libs = %{version}-%{release} # Needed by virt-pki-validate script. Requires: gnutls-utils + %if %{with_ssh_proxy} +Recommends: libvirt-ssh-proxy = %{version}-%{release} + %endif
I wonder if we should also have this present as a stronger 'Requires' in both libvirt-daemon-kvm & libvirt-daemon-qemu, as those are convenience RPMs intended to provide a set of RPMs to maxmimise the featureset.
diff --git a/meson.build b/meson.build index 1518afa1cb..b19f9b1ed1 100644 --- a/meson.build +++ b/meson.build @@ -113,6 +113,11 @@ endif confdir = sysconfdir / meson.project_name() pkgdatadir = datadir / meson.project_name()
+sshconfdir = get_option('sshconfdir') +if sshconfdir == '' + sshconfdir = sysconfdir / 'ssh/ssh_config.d'
Lets use the / operator consistently there sshconfdir = sysconfdir / 'ssh' / 'ssh_config.d'
diff --git a/tools/ssh-proxy/30-libvirt-ssh-proxy.conf.in b/tools/ssh-proxy/30-libvirt-ssh-proxy.conf.in new file mode 100644 index 0000000000..9a022826f7 --- /dev/null +++ b/tools/ssh-proxy/30-libvirt-ssh-proxy.conf.in @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +Host qemu/* + ProxyCommand @libexecdir@/libvirt-ssh-proxy %h %p + ProxyUseFdpass yes + CheckHostIP no + + # Disable all kinds of host identity checks, since these addresses are generally ephemeral. + StrictHostKeyChecking no + UserKnownHostsFile /dev/null
I know systemd did this too, but I'm somewhat on the fence over whether turning this off is a good idea or not. I would assume it is recording "qemu/guestname" as the entry in the known hosts file, which is not that ephemeral for traditional guests ? I wonder if we should just let the user decide to turn this off if they so wish rather than forcing a less secure config by default.
+static int +processVsock(const char *domname, + unsigned int port) +{ + const char *uris[] = {"qemu:///system", "qemu:///session"};
We should be skipping the session driver, if running as root.
+ struct sockaddr_vm sa = { + .svm_family = AF_VSOCK, + .svm_port = port, + }; + VIR_AUTOCLOSE fd = -1; + unsigned long long cid = -1; + size_t i; + + for (i = 0; i < G_N_ELEMENTS(uris); i++) { + g_autoptr(virConnect) conn = NULL; + g_autoptr(virDomain) dom = NULL; + + if (!(dom = lookupDomain(domname, uris[i], &conn))) + continue; + + if (extractCID(dom, &cid) >= 0) + break; + } + + if (cid == -1) { + ERROR(_("No usable vsock found")); + return -1; + } + + sa.svm_cid = cid; + + fd = socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (fd < 0) { + SYS_ERROR(_("Failed to allocate AF_VSOCK socket")); + return -1; + } + + if (connect(fd, (const struct sockaddr *)&sa, sizeof(sa)) < 0) { + SYS_ERROR(_("Failed to connect to vsock (cid=%1$llu port=%2$u)"), + cid, port); + return -1; + } + + /* OpenSSH wants us to send a single byte along with the file descriptor, + * hence do so. */ + if (virSocketSendFD(STDOUT_FILENO, fd) < 0) { + SYS_ERROR(_("Failed to send file descriptor %1$d"), fd); + return -1; + } + + return 0; +}
With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|