[PATCH v2 0/3] Introduce SSH proxy

v2 of: https://lists.libvirt.org/archives/list/devel@lists.libvirt.org/thread/QYFHZ... diff to v1: - rebased onto current master - Worked in Dan's review Michal Prívozník (3): tools: Introduce SSH proxy docs: Document SSH proxy NEWS: Document SSH proxy feature NEWS.rst | 5 + docs/docs.rst | 3 + docs/meson.build | 1 + docs/nss.rst | 7 + docs/ssh-proxy.rst | 68 ++++++ libvirt.spec.in | 33 +++ meson.build | 16 +- meson_options.txt | 2 + po/POTFILES | 1 + tools/meson.build | 2 + tools/ssh-proxy/30-libvirt-ssh-proxy.conf.in | 6 + tools/ssh-proxy/meson.build | 25 ++ tools/ssh-proxy/ssh-proxy.c | 239 +++++++++++++++++++ 13 files changed, 407 insertions(+), 1 deletion(-) create mode 100644 docs/ssh-proxy.rst 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 -- 2.43.2

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 | 33 +++ meson.build | 16 +- meson_options.txt | 2 + po/POTFILES | 1 + tools/meson.build | 2 + tools/ssh-proxy/30-libvirt-ssh-proxy.conf.in | 6 + tools/ssh-proxy/meson.build | 25 ++ tools/ssh-proxy/ssh-proxy.c | 239 +++++++++++++++++++ 8 files changed, 323 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 diff --git a/libvirt.spec.in b/libvirt.spec.in index 88c62f6d92..521ecebf05 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -91,6 +91,7 @@ # Other optional features %define with_numactl 0%{!?_without_numactl:1} %define with_userfaultfd_sysctl 0%{!?_without_userfaultfd_sysctl:1} +%define with_ssh_proxy 0%{!?_without_ssh_proxy:1} # A few optional bits off by default, we enable later %define with_fuse 0 @@ -903,6 +904,9 @@ Requires: libvirt-daemon-driver-nodedev = %{version}-%{release} Requires: libvirt-daemon-driver-nwfilter = %{version}-%{release} Requires: libvirt-daemon-driver-secret = %{version}-%{release} Requires: libvirt-daemon-driver-storage = %{version}-%{release} + %if %{with_ssh_proxy} +Requires: libvirt-ssh-proxy = %{version}-%{release} + %endif Requires: qemu %description daemon-qemu @@ -931,6 +935,9 @@ Requires: libvirt-daemon-driver-nodedev = %{version}-%{release} Requires: libvirt-daemon-driver-nwfilter = %{version}-%{release} Requires: libvirt-daemon-driver-secret = %{version}-%{release} Requires: libvirt-daemon-driver-storage = %{version}-%{release} + %if %{with_ssh_proxy} +Requires: libvirt-ssh-proxy = %{version}-%{release} + %endif Requires: qemu-kvm %description daemon-kvm @@ -1018,6 +1025,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 # Ensure smooth upgrades Obsoletes: libvirt-bash-completion < 7.3.0 @@ -1100,6 +1110,15 @@ Requires: libvirt-daemon-driver-network = %{version}-%{release} Libvirt plugin for NSS for translating domain names into IP addresses. %endif +%if %{with_ssh_proxy} +%package ssh-proxy +Summary: Libvirt SSH proxy +Requires: libvirt-libs = %{version}-%{release} + +%description ssh-proxy +Allows SSH into domains via VSOCK without need for network. +%endif + %if %{with_mingw32} %package -n mingw32-libvirt Summary: %{summary} @@ -1291,6 +1310,12 @@ exit 1 %define arg_userfaultfd_sysctl -Duserfaultfd_sysctl=disabled %endif +%if %{with_ssh_proxy} + %define arg_ssh_proxy -Dssh_proxy=enabled +%else + %define arg_ssh_proxy -Dssh_proxy=disabled +%endif + %define when %(date +"%%F-%%T") %define where %(hostname) %define who %{?packager}%{!?packager:Unknown} @@ -1372,6 +1397,7 @@ export SOURCE_DATE_EPOCH=$(stat --printf='%Y' %{_specdir}/libvirt.spec) -Dtls_priority=%{tls_priority} \ -Dsysctl_config=enabled \ %{?arg_userfaultfd_sysctl} \ + %{?arg_ssh_proxy} \ %{?enable_werror} \ -Dexpensive_tests=enabled \ -Dinit_script=systemd \ @@ -1456,6 +1482,7 @@ export SOURCE_DATE_EPOCH=$(stat --printf='%Y' %{_specdir}/libvirt.spec) -Dstorage_zfs=disabled \ -Dsysctl_config=disabled \ -Duserfaultfd_sysctl=disabled \ + -Dssh_proxy=disabled \ -Dtests=disabled \ -Dudev=disabled \ -Dwireshark_dissector=disabled \ @@ -2426,6 +2453,12 @@ exit 0 %{_libdir}/libnss_libvirt.so.2 %{_libdir}/libnss_libvirt_guest.so.2 + %if %{with_ssh_proxy} +%files ssh-proxy +%config(noreplace) %{_sysconfdir}/ssh/ssh_config.d/30-libvirt-ssh-proxy.conf +%{_libexecdir}/libvirt-ssh-proxy + %endif + %if %{with_lxc} %files login-shell %attr(4750, root, virtlogin) %{_bindir}/virt-login-shell diff --git a/meson.build b/meson.build index e8b0094b91..f642247794 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' +endif + # generate configmake.h header @@ -690,12 +695,14 @@ if host_machine.system() == 'linux' symbols += [ # process management [ 'sys/syscall.h', 'SYS_pidfd_open' ], + # vsock + [ 'linux/vm_sockets.h', 'struct sockaddr_vm', '#include <sys/socket.h>' ], ] endif foreach symbol : symbols if cc.has_header_symbol(symbol[0], symbol[1], args: '-D_GNU_SOURCE', prefix: symbol.get(2, '')) - conf.set('WITH_DECL_@0@'.format(symbol[1].to_upper()), 1) + conf.set('WITH_DECL_@0@'.format(symbol[1].underscorify().to_upper()), 1) endif endforeach @@ -2033,6 +2040,12 @@ if not get_option('pm_utils').disabled() endif endif +if not get_option('ssh_proxy').disabled() and conf.has('WITH_DECL_STRUCT_SOCKADDR_VM') + conf.set('WITH_SSH_PROXY', 1) +elif get_option('ssh_proxy').enabled() + error('ssh proxy requires vm_sockets.h which wasn\'t found') +endif + if not get_option('sysctl_config').disabled() and host_machine.system() == 'linux' conf.set('WITH_SYSCTL', 1) elif get_option('sysctl_config').enabled() @@ -2344,6 +2357,7 @@ misc_summary = { 'virt-login-shell': conf.has('WITH_LOGIN_SHELL'), 'virt-host-validate': conf.has('WITH_HOST_VALIDATE'), 'TLS priority': conf.get_unquoted('TLS_PRIORITY'), + 'SSH proxy': conf.has('WITH_SSH_PROXY'), 'sysctl config': conf.has('WITH_SYSCTL'), 'userfaultfd sysctl': conf.has('WITH_USERFAULTFD_SYSCTL'), } diff --git a/meson_options.txt b/meson_options.txt index ed91d97abf..35af27306d 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -40,6 +40,7 @@ option('sanlock', type: 'feature', value: 'auto', description: 'sanlock support' option('sasl', type: 'feature', value: 'auto', description: 'sasl support') option('selinux', type: 'feature', value: 'auto', description: 'selinux support') option('selinux_mount', type: 'string', value: '', description: 'set SELinux mount point') +option('sshconfdir', type: 'string', value: '', description: 'directory for SSH client configuration') option('udev', type: 'feature', value: 'auto', description: 'udev support') option('wireshark_dissector', type: 'feature', value: 'auto', description: 'wireshark support') option('wireshark_plugindir', type: 'string', value: '', description: 'wireshark plugins directory for use when installing wireshark plugin') @@ -107,6 +108,7 @@ option('numad', type: 'feature', value: 'auto', description: 'use numad to manag option('nbdkit', type: 'feature', value: 'auto', description: 'Build nbdkit storage backend') option('nbdkit_config_default', type: 'feature', value: 'auto', description: 'Whether to use nbdkit storage backend for network disks by default (configurable)') option('pm_utils', type: 'feature', value: 'auto', description: 'use pm-utils for power management') +option('ssh_proxy', type: 'feature', value: 'auto', description: 'Build ssh-proxy for ssh over vsock') option('sysctl_config', type: 'feature', value: 'auto', description: 'Whether to install sysctl configs') option('userfaultfd_sysctl', type: 'feature', value: 'auto', description: 'Whether to install sysctl config for enabling unprivileged userfaultfd') option('tls_priority', type: 'string', value: 'NORMAL', description: 'set the default TLS session priority string') diff --git a/po/POTFILES b/po/POTFILES index 6fbff4bef2..cec7e4abf4 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -359,6 +359,7 @@ src/vz/vz_utils.c src/vz/vz_utils.h tests/virpolkittest.c tools/libvirt-guests.sh.in +tools/ssh-proxy/ssh-proxy.c tools/virsh-backup.c tools/virsh-checkpoint.c tools/virsh-completer-host.c diff --git a/tools/meson.build b/tools/meson.build index 15be557dfe..1bb84be0be 100644 --- a/tools/meson.build +++ b/tools/meson.build @@ -343,3 +343,5 @@ endif if wireshark_dep.found() subdir('wireshark') endif + +subdir('ssh-proxy') 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..cd19bdbc95 --- /dev/null +++ b/tools/ssh-proxy/30-libvirt-ssh-proxy.conf.in @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +Host qemu/* + ProxyCommand @libexecdir@/libvirt-ssh-proxy %h %p + ProxyUseFdpass yes + CheckHostIP no diff --git a/tools/ssh-proxy/meson.build b/tools/ssh-proxy/meson.build new file mode 100644 index 0000000000..e9f312fa25 --- /dev/null +++ b/tools/ssh-proxy/meson.build @@ -0,0 +1,25 @@ +if conf.has('WITH_SSH_PROXY') + executable( + 'libvirt-ssh-proxy', + [ + 'ssh-proxy.c' + ], + dependencies: [ + src_dep, + ], + link_with: [ + libvirt_lib, + ], + install: true, + install_dir: libexecdir, + install_rpath: libvirt_rpath, + ) + + configure_file( + input : '30-libvirt-ssh-proxy.conf.in', + output: '@BASENAME@', + configuration: tools_conf, + install: true, + install_dir : sshconfdir, + ) +endif diff --git a/tools/ssh-proxy/ssh-proxy.c b/tools/ssh-proxy/ssh-proxy.c new file mode 100644 index 0000000000..207d0488fb --- /dev/null +++ b/tools/ssh-proxy/ssh-proxy.c @@ -0,0 +1,239 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * For given domain and port create a VSOCK socket and pass it onto STDOUT. + */ + +#include <config.h> + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/socket.h> +#include <linux/vm_sockets.h> + +#include "internal.h" +#include "virsocket.h" +#include "virstring.h" +#include "virfile.h" +#include "datatypes.h" +#include "virgettext.h" +#include "virxml.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +#define SYS_ERROR(...) \ +do { \ + int err = errno; \ + fprintf(stderr, "ERROR %s:%d : ", __FUNCTION__, __LINE__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, " : %s\n", g_strerror(err)); \ + fprintf(stderr, "\n"); \ +} while (0) + +#define ERROR(...) \ +do { \ + fprintf(stderr, "ERROR %s:%d : ", __FUNCTION__, __LINE__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ +} while (0) + +#define HOSTNAME_PREFIX "qemu/" + +static void +dummyErrorHandler(void *opaque G_GNUC_UNUSED, + virErrorPtr error G_GNUC_UNUSED) +{ + +} + +static void +printUsage(const char *argv0) +{ + const char *progname; + + if (!(progname = strrchr(argv0, '/'))) + progname = argv0; + else + progname++; + + printf(_("\n" + "Usage:\n" + "%1$s hostname port\n" + "\n" + "Hostname should be in form '%2$s$domname'\n"), + progname, HOSTNAME_PREFIX); +} + +static int +parseArgs(int argc, + char *argv[], + const char **domname, + unsigned int *port) +{ + if (argc != 3 || + !(*domname = STRSKIP(argv[1], HOSTNAME_PREFIX))) { + ERROR(_("Bad usage")); + printUsage(argv[0]); + return -1; + } + + if (virStrToLong_ui(argv[2], NULL, 10, port) < 0) { + ERROR(_("Unable to parse port: %1$s"), argv[2]); + printUsage(argv[0]); + return -1; + } + + return 0; +} + +static virDomainPtr +lookupDomain(const char *domname, + const char *uri, + virConnectPtr *connRet) +{ + g_autoptr(virConnect) conn = NULL; + virDomainPtr dom = NULL; + + if (!(conn = virConnectOpenReadOnly(uri))) + return NULL; + + dom = virDomainLookupByName(conn, domname); + if (!dom) + dom = virDomainLookupByUUIDString(conn, domname); + if (!dom) { + int id; + + if (virStrToLong_i(domname, NULL, 10, &id) >= 0) + dom = virDomainLookupByID(conn, id); + } + if (!dom) + return NULL; + + *connRet = g_steal_pointer(&conn); + return dom; +} + + +#define VSOCK_CID_XPATH "/domain/devices/vsock/cid" + +static int +extractCID(virDomainPtr dom, + unsigned long long *cidRet) +{ + g_autofree char *domxml = NULL; + g_autoptr(xmlDoc) doc = NULL; + g_autoptr(xmlXPathContext) ctxt = NULL; + g_autofree xmlNodePtr *nodes = NULL; + int nnodes = 0; + size_t i; + + if (!(domxml = virDomainGetXMLDesc(dom, 0))) + return -1; + + doc = virXMLParseStringCtxtWithIndent(domxml, "domain", &ctxt); + if (!doc) + return -1; + + if ((nnodes = virXPathNodeSet(VSOCK_CID_XPATH, ctxt, &nodes)) < 0) { + return -1; + } + + for (i = 0; i < nnodes; i++) { + unsigned long long cid; + + if (virXMLPropULongLong(nodes[i], "address", 10, 0, &cid) > 0) { + *cidRet = cid; + return 0; + } + } + + return -1; +} + +#undef VSOCK_CID_XPATH + +static int +processVsock(const char *domname, + unsigned int port) +{ + const char *uris[] = {"qemu:///system", "qemu:///session"}; + struct sockaddr_vm sa = { + .svm_family = AF_VSOCK, + .svm_port = port, + }; + VIR_AUTOCLOSE fd = -1; + const uid_t userid = geteuid(); + 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 (userid == 0 && + STREQ(uris[i], "qemu:///session")) { + continue; + } + + 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; +} + +int main(int argc, char *argv[]) +{ + const char *domname = NULL; + unsigned int port; + + if (virGettextInitialize() < 0) + return EXIT_FAILURE; + + if (virInitialize() < 0) { + ERROR(_("Failed to initialize libvirt")); + return EXIT_FAILURE; + } + + virSetErrorFunc(NULL, dummyErrorHandler); + + if (parseArgs(argc, argv, &domname, &port) < 0) + return EXIT_FAILURE; + + if (processVsock(domname, port) < 0) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} -- 2.43.2

On Tue, May 07, 2024 at 01:08:00PM +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].
Thinking again, I'm not too comfortable about having both system and session on the same namespace, as when there is an inevitable naming clash, it is tedious to resolve (no human likes using UUIDs). How about allowing: ssh user@qemu:system/machineName ssh user@qemu:session/machineName as unambiguous options, while leaving ssh user@qemu/machineName as the simplified "(mostly) do the right thing" option
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 | 33 +++ meson.build | 16 +- meson_options.txt | 2 + po/POTFILES | 1 + tools/meson.build | 2 + tools/ssh-proxy/30-libvirt-ssh-proxy.conf.in | 6 + tools/ssh-proxy/meson.build | 25 ++ tools/ssh-proxy/ssh-proxy.c | 239 +++++++++++++++++++ 8 files changed, 323 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
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 :|

Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- docs/docs.rst | 3 ++ docs/meson.build | 1 + docs/nss.rst | 7 +++++ docs/ssh-proxy.rst | 68 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+) create mode 100644 docs/ssh-proxy.rst diff --git a/docs/docs.rst b/docs/docs.rst index f57164b9e3..1a958e9cc7 100644 --- a/docs/docs.rst +++ b/docs/docs.rst @@ -47,6 +47,9 @@ Deployment / operation `Hooks <hooks.html>`__ Hooks for system specific management +`SSH Proxy <ssh-proxy.html>`__ + Enable SSH into guests over a VSOCK + `NSS module <nss.html>`__ Enable domain host name translation to IP addresses diff --git a/docs/meson.build b/docs/meson.build index 87d728213c..2dda59f978 100644 --- a/docs/meson.build +++ b/docs/meson.build @@ -97,6 +97,7 @@ docs_rst_files = [ 'python', 'remote', 'securityprocess', + 'ssh-proxy', 'storage', 'strategy', 'styleguide', diff --git a/docs/nss.rst b/docs/nss.rst index 8f98330221..53955a3278 100644 --- a/docs/nss.rst +++ b/docs/nss.rst @@ -152,3 +152,10 @@ If there's no record for either of the aforementioned commands, it's very likely that NSS module won't find anything and vice versa. As of ``v3.0.0`` libvirt provides ``libvirt_guest`` NSS module that doesn't have this limitation. However, the statement is still true for the ``libvirt`` NSS module. + +Alternatives +------------ + +As of ``v10.3.0`` libvirt implements an `SSH proxy <ssh-proxy.html>`__ which +doesn't require any network interface to SSH into the guest as SSH flows +through a VSOCK device. diff --git a/docs/ssh-proxy.rst b/docs/ssh-proxy.rst new file mode 100644 index 0000000000..830668f8cd --- /dev/null +++ b/docs/ssh-proxy.rst @@ -0,0 +1,68 @@ +================= +Libvirt SSH proxy +================= + +Sometimes it's necessary to run some commands inside a guest. While libvirt +already provides a `NSS module <nss.html>`__ that can translate guest name to +IP address it has some limitations (e.g. guest has to have a network interface +plugged into a libvirt managed network). To resolve some of these limitations, +libvirt offers a SSH proxy. It consists of a SSH client config file +(``/etc/ssh/ssh_config.d/30-libvirt-ssh-proxy.conf``) and a small binary. Both +are automatically installed by ``libvirt-client`` package. After running: + +``ssh user@qemu/virtualMachine`` + +the configuration file instructs SSH client to start the binary helper which +finds a VSOCK device inside the ``virtualMachine`` and establishes a connection +to it. + +For now, only QEMU domains are implemented and the lookup of the +``virtualMachine`` is done under ``qemu:///system`` URI first, followed by +``qemu:///session``. Accepted values for ``virtualMachine`` are: domain name +(as reported by e.g. `virsh list`), domain UUID and finally domain ID. + +Guest OS requirements +--------------------- + +It is obvious that the SSH daemon inside the guest needs to be configured to +listen for incoming connections on a VSOCK. There are couple of ways to achieve +this: + +* Run systemd-v256 or newer inside the guest. + + In this release, systemd started to deploy ``systemd-ssh-generator`` which + should configure socket activation for SSHD automagically. + +* Set up socket activation for VSOCK. + + We can take an inspiration in the unit file generated by + ``systemd-ssh-generator``: + +:: + + [Unit] + Description=OpenSSH Server Socket (systemd-ssh-generator, AF_VSOCK) + Documentation=man:systemd-ssh-generator(8) + Wants=ssh-access.target + Before=ssh-access.target + + [Socket] + ListenStream=vsock::22 + Accept=yes + PollLimitIntervalSec=30s + PollLimitBurst=50 + +* Run a service that forwards VSOCK <=> SSHD communication + + For instance: + +:: + + socat VSOCK-LISTEN:22,reuseaddr,fork TCP:localhost:22 + +Libvirt domain XML configuration +-------------------------------- + +Since the SSH proxy uses a VSOCK to communicate with the SSH daemon running +inside the guest, it is a must to configure VSOCK in the `domain XML +<formatdomain.html#vsock>`__. -- 2.43.2

Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- NEWS.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/NEWS.rst b/NEWS.rst index d72c15bf10..ef0e4f8b51 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -22,6 +22,11 @@ v10.4.0 (unreleased) It is now possible to set on/off ``ras`` feature in the domain XML for virt (Arm) machine type as ``<ras state='on'/>``. + * SSH proxy for VM + + Libvirt now installs a binary helper that allows connecting to QEMU domains + via SSH using the following scheme: ``ssh user@qemu/virtualMachine``. + * **Improvements** * **Bug fixes** -- 2.43.2
participants (2)
-
Daniel P. Berrangé
-
Michal Privoznik