This wires up support for using the new virt-ssh-helper binary with the ssh, libssh and libssh2 protocols. The new binary will be used preferentially if it is available in $PATH, otherwise we fall back to traditional netcat. The "proxy" URI parameter can be used to force use of netcat e.g. qemu+ssh://host/system?proxy=netcat or the disable fallback e.g. qemu+ssh://host/system?proxy=native With use of virt-ssh-helper, we can now support remote session URIs qemu+ssh://host/session and this will only use virt-ssh-helper, with no fallback. This also lets the libvirtd process be auto-started, and connect directly to the modular daemons, avoiding use of virtproxyd back-compat tunnelling. Signed-off-by: Daniel P. Berrangé <berrange@xxxxxxxxxx> --- docs/uri.html.in | 24 +++++++++- src/remote/remote_driver.c | 30 +++++++++++- src/remote/remote_sockets.c | 8 ---- src/rpc/virnetclient.c | 91 ++++++++++++++++++++++++++++++------- src/rpc/virnetclient.h | 30 ++++++++++-- tests/virnetsockettest.c | 7 ++- 6 files changed, 156 insertions(+), 34 deletions(-) diff --git a/docs/uri.html.in b/docs/uri.html.in index 49f92773f8..4cce2d6a48 100644 --- a/docs/uri.html.in +++ b/docs/uri.html.in @@ -259,6 +259,24 @@ Note that parameter values must be <td colspan="2"/> <td> Example: <code>mode=direct</code> </td> </tr> + <tr> + <td> + <code>proxy</code> + </td> + <td>auto, virt, generic </td> + <td> + <dl> + <dt><code>auto</code></dt><dd>try native, fallback to netcat</dd> + <dt><code>netcat</code></dt><dd>only use netcat</dd> + <dt><code>native</code></dt><dd>only use native</dd> + </dl> + Can also be set in <code>libvirt.conf</code> as <code>remote_proxy</code> + </td> + </tr> + <tr> + <td colspan="2"/> + <td> Example: <code>proxy=native</code> </td> + </tr> <tr> <td> <code>command</code> @@ -296,8 +314,10 @@ Note that parameter values must be <td> ssh, libssh2, libssh </td> <td> The name of the netcat command on the remote machine. - The default is <code>nc</code>. For ssh transport, libvirt - constructs an ssh command which looks like: + The default is <code>nc</code>. This is not permitted + when using the <code>native</code> proxy mode. For ssh + transport, libvirt constructs an ssh command which looks + like: <pre><i>command</i> -p <i>port</i> [-l <i>username</i>] <i>hostname</i> <i>netcat</i> -U <i>socket</i> </pre> diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index d56f4b7260..3058849153 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -761,12 +761,14 @@ doRemoteOpen(virConnectPtr conn, g_autofree char *knownHosts = NULL; g_autofree char *mode_str = NULL; g_autofree char *daemon_name = NULL; + g_autofree char *proxy_str = NULL; bool sanity = true; bool verify = true; #ifndef WIN32 bool tty = true; #endif int mode; + int proxy; if (inside_daemon && !conn->uri->server) { mode = REMOTE_DRIVER_MODE_DIRECT; @@ -774,6 +776,14 @@ doRemoteOpen(virConnectPtr conn, mode = REMOTE_DRIVER_MODE_AUTO; } + /* Historically we didn't allow ssh tunnel with session mode, + * since we can't construct the accurate path remotely, + * so we can default to modern virt-ssh-helper */ + if (flags & VIR_DRV_OPEN_REMOTE_USER) + proxy = VIR_NET_CLIENT_PROXY_NATIVE; + else + proxy = VIR_NET_CLIENT_PROXY_NETCAT; + /* We handle *ALL* URIs here. The caller has rejected any * URIs we don't care about */ @@ -813,6 +823,7 @@ doRemoteOpen(virConnectPtr conn, EXTRACT_URI_ARG_STR("known_hosts_verify", knownHostsVerify); EXTRACT_URI_ARG_STR("tls_priority", tls_priority); EXTRACT_URI_ARG_STR("mode", mode_str); + EXTRACT_URI_ARG_STR("proxy", proxy_str); EXTRACT_URI_ARG_BOOL("no_sanity", sanity); EXTRACT_URI_ARG_BOOL("no_verify", verify); #ifndef WIN32 @@ -865,6 +876,14 @@ doRemoteOpen(virConnectPtr conn, (mode = remoteDriverModeTypeFromString(mode_str)) < 0) goto failed; + if (conf && !proxy_str && + virConfGetValueString(conf, "remote_proxy", &proxy_str) < 0) + goto failed; + + if (proxy_str && + (proxy = virNetClientProxyTypeFromString(proxy_str)) < 0) + goto failed; + /* Sanity check that nothing requested !direct mode by mistake */ if (inside_daemon && !conn->uri->server && mode != REMOTE_DRIVER_MODE_DIRECT) { virReportError(VIR_ERR_INVALID_ARG, "%s", @@ -949,8 +968,11 @@ doRemoteOpen(virConnectPtr conn, knownHosts, knownHostsVerify, sshauth, + proxy, netcat, sockname, + name, + flags & VIR_DRV_OPEN_REMOTE_RO, auth, conn->uri); if (!priv->client) @@ -970,8 +992,11 @@ doRemoteOpen(virConnectPtr conn, knownHosts, knownHostsVerify, sshauth, + proxy, netcat, sockname, + name, + flags & VIR_DRV_OPEN_REMOTE_RO, auth, conn->uri); if (!priv->client) @@ -1011,8 +1036,11 @@ doRemoteOpen(virConnectPtr conn, !tty, !verify, keyfile, + proxy, netcat, - sockname))) + sockname, + name, + flags & VIR_DRV_OPEN_REMOTE_RO))) goto failed; priv->is_secure = 1; diff --git a/src/remote/remote_sockets.c b/src/remote/remote_sockets.c index 854775f401..7c69ed9e7f 100644 --- a/src/remote/remote_sockets.c +++ b/src/remote/remote_sockets.c @@ -108,14 +108,6 @@ remoteGetUNIXSocketHelper(remoteDriverTransport transport, g_autofree char *userdir = NULL; if (session) { - if (transport != REMOTE_DRIVER_TRANSPORT_UNIX) { - virReportError(VIR_ERR_OPERATION_UNSUPPORTED, - _("Connecting to session instance without " - "socket path is not supported by the %s " - "transport"), - remoteDriverTransportTypeToString(transport)); - return NULL; - } userdir = virGetUserRuntimeDirectory(); sockname = g_strdup_printf("%s/%s-sock", userdir, sock_prefix); diff --git a/src/rpc/virnetclient.c b/src/rpc/virnetclient.c index 72ece28f59..5ce6ffa686 100644 --- a/src/rpc/virnetclient.c +++ b/src/rpc/virnetclient.c @@ -50,6 +50,10 @@ enum { VIR_NET_CLIENT_MODE_COMPLETE, }; +VIR_ENUM_IMPL(virNetClientProxy, + VIR_NET_CLIENT_PROXY_LAST, + "auto", "netcat", "native"); + struct _virNetClientCall { int mode; @@ -414,23 +418,64 @@ virNetClientDoubleEscapeShell(const char *str) } char * -virNetClientSSHHelperCommand(const char *netcatPath, - const char *socketPath) -{ - g_autofree char *netcatPathSafe = virNetClientDoubleEscapeShell(netcatPath); - - if (!netcatPath) - netcatPath = "nc"; +virNetClientSSHHelperCommand(virNetClientProxy proxy, + const char *netcatPath, + const char *socketPath, + const char *driverURI, + bool readonly) +{ + g_autofree char *netcatPathSafe = virNetClientDoubleEscapeShell(netcatPath ? netcatPath : "nc"); + g_autofree char *driverURISafe = virNetClientDoubleEscapeShell(driverURI); + g_autofree char *nccmd = NULL; + g_autofree char *helpercmd = NULL; + + /* If user gave a 'netcat' path in the URI, we must + * assume they want the legacy 'nc' based proxy, not + * our new virt-ssh-helper + */ + if (proxy == VIR_NET_CLIENT_PROXY_AUTO && + netcatPath != NULL) { + proxy = VIR_NET_CLIENT_PROXY_NETCAT; + } - return g_strdup_printf( - "sh -c " - "'if '%s' -q 2>&1 | grep \"requires an argument\" >/dev/null 2>&1; then " - "ARG=-q0;" + nccmd = g_strdup_printf( + "if '%s' -q 2>&1 | grep \"requires an argument\" >/dev/null 2>&1; then " + "ARG=-q0;" "else " - "ARG=;" + "ARG=;" "fi;" - "'%s' $ARG -U %s'", + "'%s' $ARG -U %s", netcatPathSafe, netcatPathSafe, socketPath); + + helpercmd = g_strdup_printf("virt-ssh-helper%s'%s'", + readonly ? " -r " : " ", + driverURISafe); + + switch (proxy) { + case VIR_NET_CLIENT_PROXY_AUTO: + return g_strdup_printf("sh -c 'which virt-nc 1>/dev/null 2>&1; " + "if test $? = 0; then " + " %s; " + "else" + " %s; " + "fi'", helpercmd, nccmd); + + case VIR_NET_CLIENT_PROXY_NETCAT: + return g_strdup_printf("sh -c '%s'", nccmd); + + case VIR_NET_CLIENT_PROXY_NATIVE: + if (netcatPath) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("netcat path not valid with native proxy mode")); + return NULL; + } + return g_strdup_printf("sh -c '%s'", helpercmd); + + case VIR_NET_CLIENT_PROXY_LAST: + default: + virReportEnumRangeError(virNetClientProxy, proxy); + return NULL; + } } @@ -445,15 +490,19 @@ virNetClientPtr virNetClientNewSSH(const char *nodename, bool noTTY, bool noVerify, const char *keyfile, + virNetClientProxy proxy, const char *netcatPath, - const char *socketPath) + const char *socketPath, + const char *driverURI, + bool readonly) { virNetSocketPtr sock; g_autofree char *command = NULL; DEFAULT_VALUE(netcatPath, "nc"); - command = virNetClientSSHHelperCommand(netcatPath, socketPath); + command = virNetClientSSHHelperCommand(proxy, netcatPath, socketPath, + driverURI, readonly); if (virNetSocketNewConnectSSH(nodename, service, binary, username, noTTY, noVerify, keyfile, command, &sock) < 0) @@ -470,8 +519,11 @@ virNetClientPtr virNetClientNewLibSSH2(const char *host, const char *knownHostsPath, const char *knownHostsVerify, const char *authMethods, + virNetClientProxy proxy, const char *netcatPath, const char *socketPath, + const char *driverURI, + bool readonly, virConnectAuthPtr authPtr, virURIPtr uri) { @@ -510,7 +562,8 @@ virNetClientPtr virNetClientNewLibSSH2(const char *host, DEFAULT_VALUE(username, "root"); DEFAULT_VALUE(knownHostsVerify, "normal"); - command = virNetClientSSHHelperCommand(netcatPath, socketPath); + command = virNetClientSSHHelperCommand(proxy, netcatPath, socketPath, + driverURI, readonly); if (virNetSocketNewConnectLibSSH2(host, port, family, @@ -530,8 +583,11 @@ virNetClientPtr virNetClientNewLibssh(const char *host, const char *knownHostsPath, const char *knownHostsVerify, const char *authMethods, + virNetClientProxy proxy, const char *netcatPath, const char *socketPath, + const char *driverURI, + bool readonly, virConnectAuthPtr authPtr, virURIPtr uri) { @@ -570,7 +626,8 @@ virNetClientPtr virNetClientNewLibssh(const char *host, DEFAULT_VALUE(username, "root"); DEFAULT_VALUE(knownHostsVerify, "normal"); - command = virNetClientSSHHelperCommand(netcatPath, socketPath); + command = virNetClientSSHHelperCommand(proxy, netcatPath, socketPath, + driverURI, readonly); if (virNetSocketNewConnectLibssh(host, port, family, diff --git a/src/rpc/virnetclient.h b/src/rpc/virnetclient.h index 6fdc370083..4789316e32 100644 --- a/src/rpc/virnetclient.h +++ b/src/rpc/virnetclient.h @@ -30,9 +30,22 @@ #include "virobject.h" #include "viruri.h" +typedef enum { + VIR_NET_CLIENT_PROXY_AUTO, + VIR_NET_CLIENT_PROXY_NETCAT, + VIR_NET_CLIENT_PROXY_NATIVE, + + VIR_NET_CLIENT_PROXY_LAST, +} virNetClientProxy; + +VIR_ENUM_DECL(virNetClientProxy); + char * -virNetClientSSHHelperCommand(const char *netcatPath, - const char *socketPath); +virNetClientSSHHelperCommand(virNetClientProxy proxy, + const char *netcatPath, + const char *socketPath, + const char *driverURI, + bool readonly); virNetClientPtr virNetClientNewUNIX(const char *path, bool spawnDaemon, @@ -49,8 +62,11 @@ virNetClientPtr virNetClientNewSSH(const char *nodename, bool noTTY, bool noVerify, const char *keyfile, - const char *netcat, - const char *socketPath); + virNetClientProxy proxy, + const char *netcatPath, + const char *socketPath, + const char *driverURI, + bool readonly); virNetClientPtr virNetClientNewLibSSH2(const char *host, const char *port, @@ -60,8 +76,11 @@ virNetClientPtr virNetClientNewLibSSH2(const char *host, const char *knownHostsPath, const char *knownHostsVerify, const char *authMethods, + virNetClientProxy proxy, const char *netcatPath, const char *socketPath, + const char *driverURI, + bool readonly, virConnectAuthPtr authPtr, virURIPtr uri); @@ -73,8 +92,11 @@ virNetClientPtr virNetClientNewLibssh(const char *host, const char *knownHostsPath, const char *knownHostsVerify, const char *authMethods, + virNetClientProxy proxy, const char *netcatPath, const char *socketPath, + const char *driverURI, + bool readonly, virConnectAuthPtr authPtr, virURIPtr uri); diff --git a/tests/virnetsockettest.c b/tests/virnetsockettest.c index 31fe1f86f4..93835af62f 100644 --- a/tests/virnetsockettest.c +++ b/tests/virnetsockettest.c @@ -469,8 +469,11 @@ static int testSocketSSH(const void *opaque) virNetSocketPtr csock = NULL; /* Client socket */ int ret = -1; char buf[1024]; - g_autofree char *command = virNetClientSSHHelperCommand(data->netcat, - data->path); + g_autofree char *command = virNetClientSSHHelperCommand(VIR_NET_CLIENT_PROXY_AUTO, + data->netcat, + data->path, + "qemu:///session", + true); if (virNetSocketNewConnectSSH(data->nodename, data->service, -- 2.26.2