qemu: implement keyfile auth for ssh disks with nbdkit

For ssh disks that are served by nbdkit, we can support logging in with
an ssh key file. Pass the path to the configured key file and the
username to the nbdkit process.

Signed-off-by: Jonathon Jongsma <jjongsma@redhat.com>
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
This commit is contained in:
Jonathon Jongsma 2022-12-22 16:56:47 -06:00
parent 1e2fa6d524
commit 68599168ea
8 changed files with 93 additions and 11 deletions

View File

@ -7219,10 +7219,18 @@ virDomainDiskSourceNetworkParse(xmlNodePtr node,
return -1;
}
}
if (src->protocol == VIR_STORAGE_NET_PROTOCOL_SSH &&
(tmpnode = virXPathNode("./knownHosts", ctxt))) {
if (!(src->ssh_known_hosts_file = virXMLPropStringRequired(tmpnode, "path")))
return -1;
if (src->protocol == VIR_STORAGE_NET_PROTOCOL_SSH) {
if ((tmpnode = virXPathNode("./knownHosts", ctxt))) {
if (!(src->ssh_known_hosts_file = virXMLPropStringRequired(tmpnode, "path")))
return -1;
}
if ((tmpnode = virXPathNode("./identity", ctxt))) {
if (!(src->ssh_user = virXMLPropStringRequired(tmpnode, "username")))
return -1;
if (!(src->ssh_keyfile = virXMLPropStringRequired(tmpnode, "keyfile")))
return -1;
}
}
return 0;
@ -22211,8 +22219,18 @@ virDomainDiskSourceFormatNetwork(virBuffer *attrBuf,
if (src->timeout)
virBufferAsprintf(childBuf, "<timeout seconds='%llu'/>\n", src->timeout);
if (src->protocol == VIR_STORAGE_NET_PROTOCOL_SSH && src->ssh_known_hosts_file)
virBufferEscapeString(childBuf, "<knownHosts path='%s'/>\n", src->ssh_known_hosts_file);
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) {
virBufferAddLit(childBuf, "<identity");
virBufferEscapeString(childBuf, " username='%s'", src->ssh_user);
virBufferEscapeString(childBuf, " keyfile='%s'", src->ssh_keyfile);
virBufferAddLit(childBuf, "/>\n");
}
}
}

View File

@ -898,6 +898,7 @@ virStorageSourceCopy(const virStorageSource *src,
def->ssh_host_key_check_disabled = src->ssh_host_key_check_disabled;
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->nfs_user = g_strdup(src->nfs_user);
def->nfs_group = g_strdup(src->nfs_group);
@ -1177,6 +1178,7 @@ virStorageSourceClear(virStorageSource *def)
VIR_FREE(def->ssh_user);
VIR_FREE(def->ssh_known_hosts_file);
VIR_FREE(def->ssh_keyfile);
VIR_FREE(def->nfs_user);
VIR_FREE(def->nfs_group);

View File

@ -408,12 +408,11 @@ struct _virStorageSource {
bool hostcdrom; /* backing device is a cdrom */
/* passthrough variables for the ssh driver which we don't handle properly */
/* these must not be used apart from formatting the output JSON in the qemu driver */
/* ssh variables */
char *ssh_user;
bool ssh_host_key_check_disabled;
/* additional ssh variables */
char *ssh_known_hosts_file;
char *ssh_keyfile;
/* 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

View File

@ -1049,8 +1049,12 @@ qemuNbdkitProcessBuildCommandSSH(qemuNbdkitProcess *proc,
if (proc->source->auth) {
if (qemuNbdkitProcessBuildCommandAuth(proc->source->auth, cmd) < 0)
return -1;
} else if (proc->source->ssh_user) {
virCommandAddArgPair(cmd, "user", proc->source->ssh_user);
} else {
if (proc->source->ssh_keyfile)
virCommandAddArgPair(cmd, "identity", proc->source->ssh_keyfile);
if (proc->source->ssh_user)
virCommandAddArgPair(cmd, "user", proc->source->ssh_user);
}
if (proc->source->ssh_host_key_check_disabled)
@ -1171,6 +1175,10 @@ qemuNbdkitProcessStart(qemuNbdkitProcess *proc,
if (qemuExtDeviceLogCommand(driver, vm, cmd, "nbdkit") < 0)
goto error;
if (proc->source->ssh_keyfile &&
qemuSecurityDomainSetPathLabel(driver, vm, proc->source->ssh_keyfile, 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 +1264,9 @@ qemuNbdkitProcessStop(qemuNbdkitProcess *proc,
if (proc->source->ssh_known_hosts_file)
qemuSecurityDomainRestorePathLabel(driver, vm, proc->source->ssh_known_hosts_file);
if (proc->source->ssh_keyfile)
qemuSecurityDomainRestorePathLabel(driver, vm, proc->source->ssh_keyfile);
if (proc->pid < 0)
return 0;

View File

@ -0,0 +1,9 @@
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 \
user=myuser \
known-hosts=/path/to/ssh_known_hosts

View File

@ -0,0 +1,9 @@
nbdkit \
--unix /tmp/statedir-2/nbdkit-test-disk-2.socket \
--foreground ssh \
host=example.org \
port=2222 \
path=test2.img \
identity=/path/to/id_rsa \
user=myuser \
known-hosts=/path/to/ssh_known_hosts

View File

@ -299,6 +299,7 @@ mymain(void)
DO_TEST("disk-network-source-curl", QEMU_NBDKIT_CAPS_PLUGIN_CURL);
DO_TEST("disk-network-ssh", QEMU_NBDKIT_CAPS_PLUGIN_SSH);
DO_TEST("disk-network-ssh-password", QEMU_NBDKIT_CAPS_PLUGIN_SSH);
DO_TEST("disk-network-ssh-key", QEMU_NBDKIT_CAPS_PLUGIN_SSH);
cleanup:
qemuTestDriverFree(&driver);

View File

@ -0,0 +1,33 @@
<domain type='kvm'>
<name>QEMUGuest1</name>
<uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
<memory unit='KiB'>219136</memory>
<currentMemory unit='KiB'>219136</currentMemory>
<vcpu placement='static'>1</vcpu>
<os>
<type arch='x86_64' machine='pc'>hvm</type>
<boot dev='hd'/>
</os>
<clock offset='utc'/>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>destroy</on_crash>
<devices>
<disk type='network' device='disk'>
<driver name='qemu' type='raw'/>
<source protocol='ssh' name='test.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"/>
</source>
<target dev='vda' bus='virtio'/>
</disk>
<controller type='usb' index='0'/>
<controller type='pci' index='0' model='pci-root'/>
<input type='mouse' bus='ps2'/>
<input type='keyboard' bus='ps2'/>
<memballoon model='none'/>
</devices>
</domain>