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(a)redhat.com>
---
src/conf/domain_conf.c | 11 ++++++++---
src/conf/storage_source_conf.c | 1 +
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, 46 insertions(+), 9 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 08cf1be656..a70d7bf613 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -7257,8 +7257,11 @@ virDomainDiskSourceNetworkParse(xmlNodePtr node,
if (!(src->ssh_user = virXMLPropStringRequired(tmpnode,
"username")))
return -1;
- if (!(src->ssh_keyfile = virXMLPropStringRequired(tmpnode,
"keyfile")))
- return -1;
+ /* optional path to an ssh key file */
+ src->ssh_keyfile = virXMLPropString(tmpnode, "keyfile");
+
+ /* optional ssh-agent socket location */
+ src->ssh_agent = virXMLPropString(tmpnode, "agentsock");
}
}
@@ -22175,13 +22178,15 @@ 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");
if (src->ssh_user)
virBufferEscapeString(childBuf, " username='%s'",
src->ssh_user);
if (src->ssh_keyfile)
virBufferEscapeString(childBuf, " keyfile='%s'",
src->ssh_keyfile);
+ if (src->ssh_agent)
+ 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 5074d6b219..efdebffd1d 100644
--- a/src/conf/storage_source_conf.c
+++ b/src/conf/storage_source_conf.c
@@ -1172,6 +1172,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 4bebabf799..5e9a581169 100644
--- a/src/qemu/qemu_nbdkit.c
+++ b/src/qemu/qemu_nbdkit.c
@@ -1046,6 +1046,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");
@@ -1168,6 +1171,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;
@@ -1256,6 +1263,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