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> Example: <code>mode=direct</code> </td>
</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>
<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>

View File

@ -761,6 +761,7 @@ 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
@ -768,6 +769,7 @@ doRemoteOpen(virConnectPtr conn,
#endif
int mode;
size_t i;
int proxy;
if (inside_daemon && !conn->uri->server) {
mode = REMOTE_DRIVER_MODE_DIRECT;
@ -775,6 +777,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_AUTO;
/* We handle *ALL* URIs here. The caller has rejected any
* 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("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
@ -864,6 +875,17 @@ 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) {
virReportError(VIR_ERR_INVALID_ARG,
_("Unnkown proxy type '%s'"), proxy_str);
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",
@ -948,8 +970,11 @@ doRemoteOpen(virConnectPtr conn,
knownHosts,
knownHostsVerify,
sshauth,
proxy,
netcat,
sockname,
name,
flags & VIR_DRV_OPEN_REMOTE_RO,
auth,
conn->uri);
if (!priv->client)
@ -969,8 +994,11 @@ doRemoteOpen(virConnectPtr conn,
knownHosts,
knownHostsVerify,
sshauth,
proxy,
netcat,
sockname,
name,
flags & VIR_DRV_OPEN_REMOTE_RO,
auth,
conn->uri);
if (!priv->client)
@ -1010,8 +1038,11 @@ doRemoteOpen(virConnectPtr conn,
!tty,
!verify,
keyfile,
proxy,
netcat,
sockname)))
sockname,
name,
flags & VIR_DRV_OPEN_REMOTE_RO)))
goto failed;
priv->is_secure = 1;

View File

@ -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);

View File

@ -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)
virNetClientSSHHelperCommand(virNetClientProxy proxy,
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)
netcatPath = "nc";
/* 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,18 @@ 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);
if (!(command = virNetClientSSHHelperCommand(proxy, netcatPath, socketPath,
driverURI, readonly)))
return NULL;
if (virNetSocketNewConnectSSH(nodename, service, binary, username, noTTY,
noVerify, keyfile, command, &sock) < 0)
@ -470,8 +518,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 +561,9 @@ virNetClientPtr virNetClientNewLibSSH2(const char *host,
DEFAULT_VALUE(username, "root");
DEFAULT_VALUE(knownHostsVerify, "normal");
command = virNetClientSSHHelperCommand(netcatPath, socketPath);
if (!(command = virNetClientSSHHelperCommand(proxy, netcatPath, socketPath,
driverURI, readonly)))
return NULL;
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,9 @@ virNetClientPtr virNetClientNewLibssh(const char *host,
DEFAULT_VALUE(username, "root");
DEFAULT_VALUE(knownHostsVerify, "normal");
command = virNetClientSSHHelperCommand(netcatPath, socketPath);
if (!(command = virNetClientSSHHelperCommand(proxy, netcatPath, socketPath,
driverURI, readonly)))
return NULL;
if (virNetSocketNewConnectLibssh(host, port,
family,

View File

@ -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);

View File

@ -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,