It's not possible to use password-protected ssh keys directly with libvirt because libvirt doesn't have any way to prompt a user for the password. To accomodate password-protected key files, an administrator can add these keys to an ssh agent and then configure the domain with the path to the ssh-agent socket. Note that this requires an administrator or management app to configure the ssh-agent with an appropriate socket path and add the necessary keys to it. In addition, it does not currently work with selinux enabled. The ssh-agent socket would need a label that libvirt would be allowed to access rather than unconfined_t. Signed-off-by: Jonathon Jongsma <jjongsma@xxxxxxxxxx> Reviewed-by: Peter Krempa <pkrempa@xxxxxxxxxx> --- src/conf/domain_conf.c | 14 ++++++++++++-- src/conf/storage_source_conf.c | 2 ++ src/conf/storage_source_conf.h | 1 + src/qemu/qemu_nbdkit.c | 10 ++++++++++ .../disk-network-ssh-key.args.disk0 | 6 +++--- .../disk-network-ssh-key.args.disk1 | 9 +++++++++ tests/qemuxml2argvdata/disk-network-ssh-key.xml | 17 ++++++++++++++--- 7 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 tests/qemunbdkitdata/disk-network-ssh-key.args.disk1 diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 929e115bce..398f40d2be 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -7277,8 +7277,17 @@ virDomainDiskSourceNetworkParse(xmlNodePtr node, if (!(src->ssh_user = virXMLPropStringRequired(tmpnode, "username"))) return -1; - if (!(src->ssh_keyfile = virXMLPropStringRequired(tmpnode, "keyfile"))) + /* optional path to an ssh key file */ + src->ssh_keyfile = virXMLPropString(tmpnode, "keyfile"); + + /* optional ssh-agent socket location */ + src->ssh_agent = virXMLPropString(tmpnode, "agentsock"); + if (!src->ssh_keyfile && !src->ssh_agent) { + virReportError(VIR_ERR_XML_ERROR, + _("element '%1$s' requires either 'keyfile' or 'agentsock' attribute"), + tmpnode->name); return -1; + } } } @@ -22291,11 +22300,12 @@ virDomainDiskSourceFormatNetwork(virBuffer *attrBuf, if (src->protocol == VIR_STORAGE_NET_PROTOCOL_SSH) { if (src->ssh_known_hosts_file) virBufferEscapeString(childBuf, "<knownHosts path='%s'/>\n", src->ssh_known_hosts_file); - if (src->ssh_keyfile) { + if (src->ssh_keyfile || src->ssh_agent) { virBufferAddLit(childBuf, "<identity"); virBufferEscapeString(childBuf, " username='%s'", src->ssh_user); virBufferEscapeString(childBuf, " keyfile='%s'", src->ssh_keyfile); + virBufferEscapeString(childBuf, " agentsock='%s'", src->ssh_agent); virBufferAddLit(childBuf, "/>\n"); } diff --git a/src/conf/storage_source_conf.c b/src/conf/storage_source_conf.c index ce9c1f66c2..cafa031dfe 100644 --- a/src/conf/storage_source_conf.c +++ b/src/conf/storage_source_conf.c @@ -897,6 +897,7 @@ virStorageSourceCopy(const virStorageSource *src, def->ssh_user = g_strdup(src->ssh_user); def->ssh_known_hosts_file = g_strdup(src->ssh_known_hosts_file); def->ssh_keyfile = g_strdup(src->ssh_keyfile); + def->ssh_agent = g_strdup(src->ssh_agent); def->nfs_user = g_strdup(src->nfs_user); def->nfs_group = g_strdup(src->nfs_group); @@ -1174,6 +1175,7 @@ virStorageSourceClear(virStorageSource *def) VIR_FREE(def->ssh_user); VIR_FREE(def->ssh_known_hosts_file); VIR_FREE(def->ssh_keyfile); + VIR_FREE(def->ssh_agent); VIR_FREE(def->nfs_user); VIR_FREE(def->nfs_group); diff --git a/src/conf/storage_source_conf.h b/src/conf/storage_source_conf.h index 8c805664af..061faa66cb 100644 --- a/src/conf/storage_source_conf.h +++ b/src/conf/storage_source_conf.h @@ -411,6 +411,7 @@ struct _virStorageSource { bool ssh_host_key_check_disabled; char *ssh_known_hosts_file; char *ssh_keyfile; + char *ssh_agent; /* nfs_user and nfs_group store the strings passed in by the user for NFS params. * nfs_uid and nfs_gid represent the converted/looked up ID numbers which are used diff --git a/src/qemu/qemu_nbdkit.c b/src/qemu/qemu_nbdkit.c index 0393850ddc..a417146426 100644 --- a/src/qemu/qemu_nbdkit.c +++ b/src/qemu/qemu_nbdkit.c @@ -1043,6 +1043,9 @@ qemuNbdkitProcessBuildCommandSSH(qemuNbdkitProcess *proc, virCommandAddArgPair(cmd, "user", proc->source->ssh_user); } + if (proc->source->ssh_agent) + virCommandAddEnvPair(cmd, "SSH_AUTH_SOCK", proc->source->ssh_agent); + if (proc->source->ssh_host_key_check_disabled) virCommandAddArgPair(cmd, "verify-remote-host", "false"); @@ -1165,6 +1168,10 @@ qemuNbdkitProcessStart(qemuNbdkitProcess *proc, qemuSecurityDomainSetPathLabel(driver, vm, proc->source->ssh_keyfile, false) < 0) goto error; + if (proc->source->ssh_agent && + qemuSecurityDomainSetPathLabel(driver, vm, proc->source->ssh_agent, false) < 0) + goto error; + if (proc->source->ssh_known_hosts_file && qemuSecurityDomainSetPathLabel(driver, vm, proc->source->ssh_known_hosts_file, false) < 0) goto error; @@ -1253,6 +1260,9 @@ qemuNbdkitProcessStop(qemuNbdkitProcess *proc, if (proc->source->ssh_keyfile) qemuSecurityDomainRestorePathLabel(driver, vm, proc->source->ssh_keyfile); + if (proc->source->ssh_agent) + qemuSecurityDomainRestorePathLabel(driver, vm, proc->source->ssh_agent); + if (proc->pid < 0) return 0; diff --git a/tests/qemunbdkitdata/disk-network-ssh-key.args.disk0 b/tests/qemunbdkitdata/disk-network-ssh-key.args.disk0 index 0b52bfe0fb..f627700490 100644 --- a/tests/qemunbdkitdata/disk-network-ssh-key.args.disk0 +++ b/tests/qemunbdkitdata/disk-network-ssh-key.args.disk0 @@ -1,9 +1,9 @@ +SSH_AUTH_SOCK=/path/to/agent/socket \ nbdkit \ --unix /tmp/statedir-0/nbdkit-test-disk-0.socket \ --foreground ssh \ host=example.org \ port=2222 \ -path=test.img \ -identity=/path/to/id_rsa \ +path=test1.img \ user=myuser \ -known-hosts=/path/to/ssh_known_hosts +known-hosts=/path/to/ssh_known_hosts1 diff --git a/tests/qemunbdkitdata/disk-network-ssh-key.args.disk1 b/tests/qemunbdkitdata/disk-network-ssh-key.args.disk1 new file mode 100644 index 0000000000..80df9c30c6 --- /dev/null +++ b/tests/qemunbdkitdata/disk-network-ssh-key.args.disk1 @@ -0,0 +1,9 @@ +nbdkit \ +--unix /tmp/statedir-1/nbdkit-test-disk-1.socket \ +--foreground ssh \ +host=example.org \ +port=2222 \ +path=test2.img \ +identity=/path/to/id_rsa \ +user=myuser2 \ +known-hosts=/path/to/ssh_known_hosts2 diff --git a/tests/qemuxml2argvdata/disk-network-ssh-key.xml b/tests/qemuxml2argvdata/disk-network-ssh-key.xml index 81b92231fa..fda01e7e68 100644 --- a/tests/qemuxml2argvdata/disk-network-ssh-key.xml +++ b/tests/qemuxml2argvdata/disk-network-ssh-key.xml @@ -15,12 +15,23 @@ <devices> <disk type='network' device='disk'> <driver name='qemu' type='raw'/> - <source protocol='ssh' name='test.img'> + <source protocol='ssh' name='test1.img'> <host name='example.org' port='2222'/> <timeout seconds='1234'/> <readahead size='1024'/> - <identity username='myuser' keyfile='/path/to/id_rsa'/> - <knownHosts path="/path/to/ssh_known_hosts"/> + <identity username='myuser' agentsock='/path/to/agent/socket'/> + <knownHosts path="/path/to/ssh_known_hosts1"/> + </source> + <target dev='vda' bus='virtio'/> + </disk> + <disk type='network' device='disk'> + <driver name='qemu' type='raw'/> + <source protocol='ssh' name='test2.img'> + <host name='example.org' port='2222'/> + <timeout seconds='1234'/> + <readahead size='1024'/> + <identity username='myuser2' keyfile='/path/to/id_rsa'/> + <knownHosts path="/path/to/ssh_known_hosts2"/> </source> <target dev='vda' bus='virtio'/> </disk> -- 2.41.0