The new virsh commands are:
get-user-sshkeys
set-user-sshkeys
Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
---
docs/manpages/virsh.rst | 37 +++++++++
tools/virsh-domain.c | 167 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 204 insertions(+)
diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst
index bfd26e3120..18cee358fd 100644
--- a/docs/manpages/virsh.rst
+++ b/docs/manpages/virsh.rst
@@ -2636,6 +2636,21 @@ When *--timestamp* is used, a human-readable timestamp will be
printed
before the event.
+get-user-sshkeys
+----------------
+
+**Syntax:**
+
+::
+
+ get-user-sshkeys domain user
+
+Print SSH authorized keys for given *user* in the guest *domain*. Please note,
+that an entry in the file has internal structure as defined by *sshd(8)* and
+virsh/libvirt does handle keys as opaque strings, i.e. does not interpret
+them.
+
+
guest-agent-timeout
-------------------
@@ -4004,6 +4019,28 @@ For QEMU/KVM, this requires the guest agent to be configured
and running.
+set-user-sshkeys
+----------------
+
+**Syntax:**
+
+::
+
+ set-user-sshkeys domain user [--file FILE] [{--append | --remove}]
+
+Replace the contents of *user*'s SSH authorized keys file in the guest *domain*
+with keys read from optional *FILE*. In the *FILE* keys must be on separate
+lines and each line must follow authorized keys format as defined by *sshd(8)*.
+If no *FILE* is specified then the guest authorized keys file is cleared out.
+
+If *--append* is specified, then the guest authorized keys file content is not
+replaced and new keys from *FILE* are just appended.
+
+If *--remove* is specified, then instead of adding any new keys then keys read
+from *FILE* are removed from the authorized keys file. It is not considered an
+error if the key does not exist in the file.
+
+
setmaxmem
---------
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c
index 12b35c037d..2a775fd277 100644
--- a/tools/virsh-domain.c
+++ b/tools/virsh-domain.c
@@ -14263,6 +14263,161 @@ cmdGuestInfo(vshControl *ctl, const vshCmd *cmd)
return ret;
}
+/*
+ * "get-user-sshkeys" command
+ */
+static const vshCmdInfo info_get_user_sshkeys[] = {
+ {.name = "help",
+ .data = N_("list authorized SSH keys for given user (via agent)")
+ },
+ {.name = "desc",
+ .data = N_("Use the guest agent to query authorized SSH keys for given "
+ "user")
+ },
+ {.name = NULL}
+};
+
+static const vshCmdOptDef opts_get_user_sshkeys[] = {
+ VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
+ {.name = "user",
+ .type = VSH_OT_DATA,
+ .flags = VSH_OFLAG_REQ,
+ .help = N_("user to list authorized keys for"),
+ },
+ {.name = NULL}
+};
+
+static bool
+cmdGetUserSSHKeys(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom = NULL;
+ const char *user;
+ char **keys = NULL;
+ int nkeys = 0;
+ size_t i;
+ const unsigned int flags = 0;
+ vshTablePtr table = NULL;
+ bool ret = false;
+
+ if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ if (vshCommandOptStringReq(ctl, cmd, "user", &user) < 0)
+ goto cleanup;
+
+ nkeys = virDomainAuthorizedSSHKeysGet(dom, user, &keys, flags);
+ if (nkeys < 0)
+ goto cleanup;
+
+ if (!(table = vshTableNew(_("key"), NULL)))
+ goto cleanup;
+
+ for (i = 0; i < nkeys; i++) {
+ if (vshTableRowAppend(table, keys[i], NULL) < 0)
+ goto cleanup;
+ }
+
+ vshTablePrintToStdout(table, ctl);
+
+ ret = true;
+ cleanup:
+ vshTableFree(table);
+ if (nkeys > 0)
+ virStringListFreeCount(keys, nkeys);
+ virshDomainFree(dom);
+ return ret;
+}
+
+
+/*
+ * "set-user-sshkeys" command
+ */
+static const vshCmdInfo info_set_user_sshkeys[] = {
+ {.name = "help",
+ .data = N_("manipulate authorized SSH keys file for given user (via
agent)")
+ },
+ {.name = "desc",
+ .data = N_("Append, reset or remove specified key from the authorized "
+ "keys file for given user")
+ },
+ {.name = NULL}
+};
+
+static const vshCmdOptDef opts_set_user_sshkeys[] = {
+ VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE),
+ {.name = "user",
+ .type = VSH_OT_DATA,
+ .flags = VSH_OFLAG_REQ,
+ .help = N_("user to set authorized keys for"),
+ },
+ {.name = "file",
+ .type = VSH_OT_STRING,
+ .help = N_("optional file to read keys from"),
+ },
+ {.name = "append",
+ .type = VSH_OT_BOOL,
+ .help = N_("append keys to the authorized keys file"),
+ },
+ {.name = "remove",
+ .type = VSH_OT_BOOL,
+ .help = N_("remove keys from the authorized keys file"),
+ },
+ {.name = NULL}
+};
+
+static bool
+cmdSetUserSSHKeys(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom = NULL;
+ const char *user;
+ const char *from;
+ g_autofree char *buffer = NULL;
+ VIR_AUTOSTRINGLIST keys = NULL;
+ int nkeys = 0;
+ unsigned int flags = 0;
+ bool ret = false;
+
+ VSH_REQUIRE_OPTION("append", "file");
+ VSH_REQUIRE_OPTION("remove", "file");
+
+ if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ if (vshCommandOptStringReq(ctl, cmd, "user", &user) < 0)
+ goto cleanup;
+
+ if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
+ goto cleanup;
+
+ if (vshCommandOptBool(cmd, "append"))
+ flags |= VIR_DOMAIN_AUTHORIZED_SSH_KEYS_SET_APPEND;
+ if (vshCommandOptBool(cmd, "remove"))
+ flags |= VIR_DOMAIN_AUTHORIZED_SSH_KEYS_SET_REMOVE;
+
+ if (from) {
+ if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) {
+ vshReportError(ctl);
+ goto cleanup;
+ }
+
+ if (!(keys = virStringSplit(buffer, "\n", -1)))
+ goto cleanup;
+
+ nkeys = virStringListLength((const char **) keys);
+ }
+
+ if (virDomainAuthorizedSSHKeysSet(dom, user,
+ (const char **) keys, nkeys, flags) < 0) {
+ goto cleanup;
+ }
+
+ ret = true;
+ cleanup:
+ virshDomainFree(dom);
+ return ret;
+}
+
+
const vshCmdDef domManagementCmds[] = {
{.name = "attach-device",
.handler = cmdAttachDevice,
@@ -14530,6 +14685,12 @@ const vshCmdDef domManagementCmds[] = {
.info = info_event,
.flags = 0
},
+ {.name = "get-user-sshkeys",
+ .handler = cmdGetUserSSHKeys,
+ .opts = opts_get_user_sshkeys,
+ .info = info_get_user_sshkeys,
+ .flags = 0
+ },
{.name = "inject-nmi",
.handler = cmdInjectNMI,
.opts = opts_inject_nmi,
@@ -14776,6 +14937,12 @@ const vshCmdDef domManagementCmds[] = {
.info = info_setLifecycleAction,
.flags = 0
},
+ {.name = "set-user-sshkeys",
+ .handler = cmdSetUserSSHKeys,
+ .opts = opts_set_user_sshkeys,
+ .info = info_set_user_sshkeys,
+ .flags = 0
+ },
{.name = "set-user-password",
.handler = cmdSetUserPassword,
.opts = opts_set_user_password,
--
2.26.2