rpc: use new virt-ssh-helper binary for remote tunnelling

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@redhat.com>
This commit is contained in:
Daniel P. Berrangé 2020-07-08 17:03:38 +01:00
parent 6e4143c851
commit f8ec7c842d
6 changed files with 160 additions and 34 deletions

View File

@ -259,6 +259,24 @@ Note that parameter values must be
<td colspan="2"/> <td colspan="2"/>
<td> Example: <code>mode=direct</code> </td> <td> Example: <code>mode=direct</code> </td>
</tr> </tr>
<tr>
<td>
<code>proxy</code>
</td>
<td>auto, netcat, native </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> <tr>
<td> <td>
<code>command</code> <code>command</code>
@ -296,8 +314,10 @@ Note that parameter values must be
<td> ssh, libssh2, libssh </td> <td> ssh, libssh2, libssh </td>
<td> <td>
The name of the netcat command on the remote machine. The name of the netcat command on the remote machine.
The default is <code>nc</code>. For ssh transport, libvirt The default is <code>nc</code>. This is not permitted
constructs an ssh command which looks like: 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><i>command</i> -p <i>port</i> [-l <i>username</i>] <i>hostname</i> <i>netcat</i> -U <i>socket</i>
</pre> </pre>

View File

@ -761,6 +761,7 @@ doRemoteOpen(virConnectPtr conn,
g_autofree char *knownHosts = NULL; g_autofree char *knownHosts = NULL;
g_autofree char *mode_str = NULL; g_autofree char *mode_str = NULL;
g_autofree char *daemon_name = NULL; g_autofree char *daemon_name = NULL;
g_autofree char *proxy_str = NULL;
bool sanity = true; bool sanity = true;
bool verify = true; bool verify = true;
#ifndef WIN32 #ifndef WIN32
@ -768,6 +769,7 @@ doRemoteOpen(virConnectPtr conn,
#endif #endif
int mode; int mode;
size_t i; size_t i;
int proxy;
if (inside_daemon && !conn->uri->server) { if (inside_daemon && !conn->uri->server) {
mode = REMOTE_DRIVER_MODE_DIRECT; mode = REMOTE_DRIVER_MODE_DIRECT;
@ -775,6 +777,14 @@ doRemoteOpen(virConnectPtr conn,
mode = REMOTE_DRIVER_MODE_AUTO; 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_AUTO;
/* We handle *ALL* URIs here. The caller has rejected any /* We handle *ALL* URIs here. The caller has rejected any
* URIs we don't care about */ * URIs we don't care about */
@ -812,6 +822,7 @@ doRemoteOpen(virConnectPtr conn,
EXTRACT_URI_ARG_STR("known_hosts_verify", knownHostsVerify); EXTRACT_URI_ARG_STR("known_hosts_verify", knownHostsVerify);
EXTRACT_URI_ARG_STR("tls_priority", tls_priority); EXTRACT_URI_ARG_STR("tls_priority", tls_priority);
EXTRACT_URI_ARG_STR("mode", mode_str); 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_sanity", sanity);
EXTRACT_URI_ARG_BOOL("no_verify", verify); EXTRACT_URI_ARG_BOOL("no_verify", verify);
#ifndef WIN32 #ifndef WIN32
@ -864,6 +875,17 @@ doRemoteOpen(virConnectPtr conn,
(mode = remoteDriverModeTypeFromString(mode_str)) < 0) (mode = remoteDriverModeTypeFromString(mode_str)) < 0)
goto failed; goto failed;
if (conf && !proxy_str &&
virConfGetValueString(conf, "remote_proxy", &proxy_str) < 0)
goto failed;
if (proxy_str &&
(proxy = virNetClientProxyTypeFromString(proxy_str)) < 0) {
virReportError(VIR_ERR_INVALID_ARG,
_("Unnkown proxy type '%s'"), proxy_str);
goto failed;
}
/* Sanity check that nothing requested !direct mode by mistake */ /* Sanity check that nothing requested !direct mode by mistake */
if (inside_daemon && !conn->uri->server && mode != REMOTE_DRIVER_MODE_DIRECT) { if (inside_daemon && !conn->uri->server && mode != REMOTE_DRIVER_MODE_DIRECT) {
virReportError(VIR_ERR_INVALID_ARG, "%s", virReportError(VIR_ERR_INVALID_ARG, "%s",
@ -948,8 +970,11 @@ doRemoteOpen(virConnectPtr conn,
knownHosts, knownHosts,
knownHostsVerify, knownHostsVerify,
sshauth, sshauth,
proxy,
netcat, netcat,
sockname, sockname,
name,
flags & VIR_DRV_OPEN_REMOTE_RO,
auth, auth,
conn->uri); conn->uri);
if (!priv->client) if (!priv->client)
@ -969,8 +994,11 @@ doRemoteOpen(virConnectPtr conn,
knownHosts, knownHosts,
knownHostsVerify, knownHostsVerify,
sshauth, sshauth,
proxy,
netcat, netcat,
sockname, sockname,
name,
flags & VIR_DRV_OPEN_REMOTE_RO,
auth, auth,
conn->uri); conn->uri);
if (!priv->client) if (!priv->client)
@ -1010,8 +1038,11 @@ doRemoteOpen(virConnectPtr conn,
!tty, !tty,
!verify, !verify,
keyfile, keyfile,
proxy,
netcat, netcat,
sockname))) sockname,
name,
flags & VIR_DRV_OPEN_REMOTE_RO)))
goto failed; goto failed;
priv->is_secure = 1; priv->is_secure = 1;

View File

@ -108,14 +108,6 @@ remoteGetUNIXSocketHelper(remoteDriverTransport transport,
g_autofree char *userdir = NULL; g_autofree char *userdir = NULL;
if (session) { 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(); userdir = virGetUserRuntimeDirectory();
sockname = g_strdup_printf("%s/%s-sock", userdir, sock_prefix); sockname = g_strdup_printf("%s/%s-sock", userdir, sock_prefix);

View File

@ -50,6 +50,10 @@ enum {
VIR_NET_CLIENT_MODE_COMPLETE, VIR_NET_CLIENT_MODE_COMPLETE,
}; };
VIR_ENUM_IMPL(virNetClientProxy,
VIR_NET_CLIENT_PROXY_LAST,
"auto", "netcat", "native");
struct _virNetClientCall { struct _virNetClientCall {
int mode; int mode;
@ -414,23 +418,64 @@ virNetClientDoubleEscapeShell(const char *str)
} }
char * char *
virNetClientSSHHelperCommand(const char *netcatPath, virNetClientSSHHelperCommand(virNetClientProxy proxy,
const char *socketPath) const char *netcatPath,
const char *socketPath,
const char *driverURI,
bool readonly)
{ {
g_autofree char *netcatPathSafe = virNetClientDoubleEscapeShell(netcatPath); 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 (!netcatPath) /* If user gave a 'netcat' path in the URI, we must
netcatPath = "nc"; * 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( nccmd = g_strdup_printf(
"sh -c " "if '%s' -q 2>&1 | grep \"requires an argument\" >/dev/null 2>&1; then "
"'if '%s' -q 2>&1 | grep \"requires an argument\" >/dev/null 2>&1; then " "ARG=-q0;"
"ARG=-q0;"
"else " "else "
"ARG=;" "ARG=;"
"fi;" "fi;"
"'%s' $ARG -U %s'", "'%s' $ARG -U %s",
netcatPathSafe, netcatPathSafe, socketPath); 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,18 @@ virNetClientPtr virNetClientNewSSH(const char *nodename,
bool noTTY, bool noTTY,
bool noVerify, bool noVerify,
const char *keyfile, const char *keyfile,
virNetClientProxy proxy,
const char *netcatPath, const char *netcatPath,
const char *socketPath) const char *socketPath,
const char *driverURI,
bool readonly)
{ {
virNetSocketPtr sock; virNetSocketPtr sock;
g_autofree char *command = NULL; g_autofree char *command = NULL;
DEFAULT_VALUE(netcatPath, "nc"); if (!(command = virNetClientSSHHelperCommand(proxy, netcatPath, socketPath,
driverURI, readonly)))
command = virNetClientSSHHelperCommand(netcatPath, socketPath); return NULL;
if (virNetSocketNewConnectSSH(nodename, service, binary, username, noTTY, if (virNetSocketNewConnectSSH(nodename, service, binary, username, noTTY,
noVerify, keyfile, command, &sock) < 0) noVerify, keyfile, command, &sock) < 0)
@ -470,8 +518,11 @@ virNetClientPtr virNetClientNewLibSSH2(const char *host,
const char *knownHostsPath, const char *knownHostsPath,
const char *knownHostsVerify, const char *knownHostsVerify,
const char *authMethods, const char *authMethods,
virNetClientProxy proxy,
const char *netcatPath, const char *netcatPath,
const char *socketPath, const char *socketPath,
const char *driverURI,
bool readonly,
virConnectAuthPtr authPtr, virConnectAuthPtr authPtr,
virURIPtr uri) virURIPtr uri)
{ {
@ -510,7 +561,9 @@ virNetClientPtr virNetClientNewLibSSH2(const char *host,
DEFAULT_VALUE(username, "root"); DEFAULT_VALUE(username, "root");
DEFAULT_VALUE(knownHostsVerify, "normal"); DEFAULT_VALUE(knownHostsVerify, "normal");
command = virNetClientSSHHelperCommand(netcatPath, socketPath); if (!(command = virNetClientSSHHelperCommand(proxy, netcatPath, socketPath,
driverURI, readonly)))
return NULL;
if (virNetSocketNewConnectLibSSH2(host, port, if (virNetSocketNewConnectLibSSH2(host, port,
family, family,
@ -530,8 +583,11 @@ virNetClientPtr virNetClientNewLibssh(const char *host,
const char *knownHostsPath, const char *knownHostsPath,
const char *knownHostsVerify, const char *knownHostsVerify,
const char *authMethods, const char *authMethods,
virNetClientProxy proxy,
const char *netcatPath, const char *netcatPath,
const char *socketPath, const char *socketPath,
const char *driverURI,
bool readonly,
virConnectAuthPtr authPtr, virConnectAuthPtr authPtr,
virURIPtr uri) virURIPtr uri)
{ {
@ -570,7 +626,9 @@ virNetClientPtr virNetClientNewLibssh(const char *host,
DEFAULT_VALUE(username, "root"); DEFAULT_VALUE(username, "root");
DEFAULT_VALUE(knownHostsVerify, "normal"); DEFAULT_VALUE(knownHostsVerify, "normal");
command = virNetClientSSHHelperCommand(netcatPath, socketPath); if (!(command = virNetClientSSHHelperCommand(proxy, netcatPath, socketPath,
driverURI, readonly)))
return NULL;
if (virNetSocketNewConnectLibssh(host, port, if (virNetSocketNewConnectLibssh(host, port,
family, family,

View File

@ -30,9 +30,22 @@
#include "virobject.h" #include "virobject.h"
#include "viruri.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 * char *
virNetClientSSHHelperCommand(const char *netcatPath, virNetClientSSHHelperCommand(virNetClientProxy proxy,
const char *socketPath); const char *netcatPath,
const char *socketPath,
const char *driverURI,
bool readonly);
virNetClientPtr virNetClientNewUNIX(const char *path, virNetClientPtr virNetClientNewUNIX(const char *path,
bool spawnDaemon, bool spawnDaemon,
@ -49,8 +62,11 @@ virNetClientPtr virNetClientNewSSH(const char *nodename,
bool noTTY, bool noTTY,
bool noVerify, bool noVerify,
const char *keyfile, const char *keyfile,
const char *netcat, virNetClientProxy proxy,
const char *socketPath); const char *netcatPath,
const char *socketPath,
const char *driverURI,
bool readonly);
virNetClientPtr virNetClientNewLibSSH2(const char *host, virNetClientPtr virNetClientNewLibSSH2(const char *host,
const char *port, const char *port,
@ -60,8 +76,11 @@ virNetClientPtr virNetClientNewLibSSH2(const char *host,
const char *knownHostsPath, const char *knownHostsPath,
const char *knownHostsVerify, const char *knownHostsVerify,
const char *authMethods, const char *authMethods,
virNetClientProxy proxy,
const char *netcatPath, const char *netcatPath,
const char *socketPath, const char *socketPath,
const char *driverURI,
bool readonly,
virConnectAuthPtr authPtr, virConnectAuthPtr authPtr,
virURIPtr uri); virURIPtr uri);
@ -73,8 +92,11 @@ virNetClientPtr virNetClientNewLibssh(const char *host,
const char *knownHostsPath, const char *knownHostsPath,
const char *knownHostsVerify, const char *knownHostsVerify,
const char *authMethods, const char *authMethods,
virNetClientProxy proxy,
const char *netcatPath, const char *netcatPath,
const char *socketPath, const char *socketPath,
const char *driverURI,
bool readonly,
virConnectAuthPtr authPtr, virConnectAuthPtr authPtr,
virURIPtr uri); virURIPtr uri);

View File

@ -469,8 +469,11 @@ static int testSocketSSH(const void *opaque)
virNetSocketPtr csock = NULL; /* Client socket */ virNetSocketPtr csock = NULL; /* Client socket */
int ret = -1; int ret = -1;
char buf[1024]; char buf[1024];
g_autofree char *command = virNetClientSSHHelperCommand(data->netcat, g_autofree char *command = virNetClientSSHHelperCommand(VIR_NET_CLIENT_PROXY_AUTO,
data->path); data->netcat,
data->path,
"qemu:///session",
true);
if (virNetSocketNewConnectSSH(data->nodename, if (virNetSocketNewConnectSSH(data->nodename,
data->service, data->service,