This wires up support for using the new virt-nc 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=virt-nc With use of virt-nc, we can now support remote session URIs qemu+ssh://host/session and this will only use virt-nc, with no fallback. This also lets the libvirtd process be auto-started. Signed-off-by: Daniel P. Berrangé <berrange@xxxxxxxxxx> --- docs/uri.html.in | 18 ++++++++++ src/remote/remote_driver.c | 30 +++++++++++++++- src/remote/remote_sockets.c | 8 ----- src/rpc/virnetclient.c | 70 ++++++++++++++++++++++++++++++------- src/rpc/virnetclient.h | 30 +++++++++++++--- tests/virnetsockettest.c | 7 ++-- 6 files changed, 136 insertions(+), 27 deletions(-) diff --git a/docs/uri.html.in b/docs/uri.html.in index 49f92773f8..5311579273 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 virt-nc, fallback to netcat</dd> + <dt><code>netcat</code></dt><dd>only use netcat</dd> + <dt><code>virt-nc</code></dt><dd>only use virt-nc</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=virt-nc</code> </td> + </tr> <tr> <td> <code>command</code> diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index c1f7a45aab..83789a86a9 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-nc */ + if (flags & VIR_DRV_OPEN_REMOTE_USER) + proxy = VIR_NET_CLIENT_PROXY_VIRT_NC; + 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 ? netcat : "nc", - 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 cd1bcc3ab3..5939f74e62 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", "virt-nc"); + struct _virNetClientCall { int mode; @@ -414,20 +418,50 @@ virNetClientDoubleEscapeShell(const char *str) } char * -virNetClientSSHHelperCommand(const char *netcatPath, - const char *socketPath) +virNetClientSSHHelperCommand(virNetClientProxy proxy, + const char *netcatPath, + const char *socketPath, + const char *driverURI, + bool readonly) { g_autofree char *netcatPathSafe = virNetClientDoubleEscapeShell(netcatPath); + g_autofree char *driverURISafe = virNetClientDoubleEscapeShell(driverURI); + g_autofree char *nccmd = NULL; + g_autofree char *virtnccmd = NULL; - 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); + + virtnccmd = g_strdup_printf("%s '%s'", + readonly ? "virt-nc -r" : "virt-nc", + 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'", virtnccmd, nccmd); + + case VIR_NET_CLIENT_PROXY_NETCAT: + return g_strdup_printf("sh -c '%s'", nccmd); + + case VIR_NET_CLIENT_PROXY_VIRT_NC: + return g_strdup_printf("sh -c '%s'", virtnccmd); + + case VIR_NET_CLIENT_PROXY_LAST: + default: + virReportEnumRangeError(virNetClientProxy, proxy); + return NULL; + } } @@ -442,8 +476,11 @@ 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; @@ -451,7 +488,8 @@ virNetClientPtr virNetClientNewSSH(const char *nodename, 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) @@ -468,8 +506,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) { @@ -511,7 +552,8 @@ virNetClientPtr virNetClientNewLibSSH2(const char *host, DEFAULT_VALUE(netcatPath, "nc"); DEFAULT_VALUE(knownHostsVerify, "normal"); - command = virNetClientSSHHelperCommand(netcatPath, socketPath); + command = virNetClientSSHHelperCommand(proxy, netcatPath, socketPath, + driverURI, readonly); if (virNetSocketNewConnectLibSSH2(host, port, family, @@ -531,8 +573,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) { @@ -574,7 +619,8 @@ virNetClientPtr virNetClientNewLibssh(const char *host, DEFAULT_VALUE(netcatPath, "nc"); 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..76500e2c3f 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_VIRT_NC, + + 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 842eb1bcfc..c6fbe479d7 100644 --- a/tests/virnetsockettest.c +++ b/tests/virnetsockettest.c @@ -464,8 +464,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