diff options
author | Luca Boccassi <bluca@debian.org> | 2024-03-22 17:28:03 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-03-22 17:28:03 +0100 |
commit | 1b5f3f5662023a8d9128214a2b094ac2e1291822 (patch) | |
tree | ab9704a2c5b5fe1bef9009e54aaa470dad670c7f | |
parent | Merge pull request #31908 from DaanDeMeyer/mkosi (diff) | |
parent | ssh-generator: support ssh.ephemeral-key.all-users (diff) | |
download | systemd-1b5f3f5662023a8d9128214a2b094ac2e1291822.tar.xz systemd-1b5f3f5662023a8d9128214a2b094ac2e1291822.zip |
Merge pull request #31670 from CodethinkLabs/vmspawn/generate_ssh_keys
vmspawn: generate ssh keys
-rw-r--r-- | man/systemd-vmspawn.xml | 27 | ||||
-rw-r--r-- | src/ssh-generator/ssh-generator.c | 5 | ||||
-rw-r--r-- | src/vmspawn/vmspawn.c | 113 |
3 files changed, 143 insertions, 2 deletions
diff --git a/man/systemd-vmspawn.xml b/man/systemd-vmspawn.xml index b75158811f..90ddc9b674 100644 --- a/man/systemd-vmspawn.xml +++ b/man/systemd-vmspawn.xml @@ -385,6 +385,33 @@ <xi:include href="version-info.xml" xpointer="v256"/> </listitem> </varlistentry> + + <varlistentry> + <term><option>--pass-ssh-key=</option><replaceable>BOOL</replaceable></term> + + <listitem><para>By default an SSH key is generated to allow <command>systemd-vmspawn</command> to open + a D-Bus connection to the VM's systemd bus. Setting this to "no" will disable SSH key generation.</para> + + <para>The generated keys are ephemeral. That is they are valid only for the current invocation of <command>systemd-vmspawn</command>, + and are typically not persisted.</para> + + <xi:include href="version-info.xml" xpointer="v256"/> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--ssh-key-type=</option><replaceable>TYPE</replaceable></term> + + <listitem><para>Configures the type of SSH key to generate, see + <citerefentry><refentrytitle>ssh-keygen</refentrytitle><manvolnum>1</manvolnum></citerefentry> + for more information.</para> + + <para>By default <literal>ed25519</literal> keys are generated, however <literal>rsa</literal> keys + may also be useful if the VM has a particularly old version of <command>sshd</command></para>. + + <xi:include href="version-info.xml" xpointer="v256"/> + </listitem> + </varlistentry> </variablelist> </refsect2> diff --git a/src/ssh-generator/ssh-generator.c b/src/ssh-generator/ssh-generator.c index 613e88dd19..5e03783543 100644 --- a/src/ssh-generator/ssh-generator.c +++ b/src/ssh-generator/ssh-generator.c @@ -108,8 +108,9 @@ static int make_sshd_template_unit( "Description=OpenSSH Per-Connection Server Daemon\n" "Documentation=man:systemd-ssh-generator(8) man:sshd(8)\n" "[Service]\n" - "ExecStart=-%s -i\n" - "StandardInput=socket", + "ExecStart=-%s -i -o \"AuthorizedKeysFile ${CREDENTIALS_DIRECTORY}/ssh.ephemeral-authorized_keys-all .ssh/authorized_keys\"\n" + "StandardInput=socket\n" + "ImportCredential=ssh.ephemeral-authorized_keys-all", sshd_binary); r = fflush_and_check(f); diff --git a/src/vmspawn/vmspawn.c b/src/vmspawn/vmspawn.c index 5ab0053be9..8d2348d748 100644 --- a/src/vmspawn/vmspawn.c +++ b/src/vmspawn/vmspawn.c @@ -13,6 +13,7 @@ #include "dirent-util.h" #include "fd-util.h" #include "discover-image.h" +#include "pidref.h" #include "sd-daemon.h" #include "sd-event.h" #include "sd-id128.h" @@ -33,6 +34,7 @@ #include "gpt.h" #include "hexdecoct.h" #include "hostname-util.h" +#include "io-util.h" #include "kernel-image.h" #include "log.h" #include "machine-credential.h" @@ -54,6 +56,7 @@ #include "stat-util.h" #include "string-util.h" #include "strv.h" +#include "time-util.h" #include "tmpfile-util.h" #include "unit-name.h" #include "vmspawn-mount.h" @@ -92,6 +95,8 @@ static sd_id128_t arg_uuid = {}; static char **arg_kernel_cmdline_extra = NULL; static char **arg_extra_drives = NULL; static char *arg_background = NULL; +static bool arg_pass_ssh_key = true; +static char *arg_ssh_key_type = NULL; STATIC_DESTRUCTOR_REGISTER(arg_directory, freep); STATIC_DESTRUCTOR_REGISTER(arg_image, freep); @@ -107,6 +112,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_forward_journal, freep); STATIC_DESTRUCTOR_REGISTER(arg_kernel_cmdline_extra, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_extra_drives, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_background, freep); +STATIC_DESTRUCTOR_REGISTER(arg_ssh_key_type, freep); static int help(void) { _cleanup_free_ char *link = NULL; @@ -158,6 +164,8 @@ static int help(void) { "\n%3$sIntegration:%4$s\n" " --forward-journal=FILE|DIR\n" " Forward the VM's journal to the host\n" + " --pass-ssh-key=BOOL Create an SSH key to access the VM\n" + " --ssh-key-type=TYPE Choose what type of SSH key to pass\n" "\n%3$sInput/Output:%4$s\n" " --console=MODE Console mode (interactive, native, gui)\n" " --background=COLOR Set ANSI color for background\n" @@ -200,6 +208,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_SECURE_BOOT, ARG_PRIVATE_USERS, ARG_FORWARD_JOURNAL, + ARG_PASS_SSH_KEY, + ARG_SSH_KEY_TYPE, ARG_SET_CREDENTIAL, ARG_LOAD_CREDENTIAL, ARG_FIRMWARE, @@ -239,6 +249,8 @@ static int parse_argv(int argc, char *argv[]) { { "secure-boot", required_argument, NULL, ARG_SECURE_BOOT }, { "private-users", required_argument, NULL, ARG_PRIVATE_USERS }, { "forward-journal", required_argument, NULL, ARG_FORWARD_JOURNAL }, + { "pass-ssh-key", required_argument, NULL, ARG_PASS_SSH_KEY }, + { "ssh-key-type", required_argument, NULL, ARG_SSH_KEY_TYPE }, { "set-credential", required_argument, NULL, ARG_SET_CREDENTIAL }, { "load-credential", required_argument, NULL, ARG_LOAD_CREDENTIAL }, { "firmware", required_argument, NULL, ARG_FIRMWARE }, @@ -442,6 +454,23 @@ static int parse_argv(int argc, char *argv[]) { return r; break; + case ARG_PASS_SSH_KEY: + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --pass-ssh-key= argument: %s", optarg); + + arg_pass_ssh_key = r; + break; + + case ARG_SSH_KEY_TYPE: + if (!string_is_safe(optarg)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid value for --arg-ssh-key-type=: %s", optarg); + + r = free_and_strdup_warn(&arg_ssh_key_type, optarg); + if (r < 0) + return r; + break; + case ARG_SET_CREDENTIAL: { r = machine_credential_set(&arg_credentials, optarg); if (r < 0) @@ -1100,11 +1129,55 @@ static void set_window_title(PTYForward *f) { if (dot) (void) pty_forward_set_title_prefix(f, dot); } +static int generate_ssh_keypair(const char *key_path, const char *key_type) { + _cleanup_free_ char *ssh_keygen = NULL; + _cleanup_strv_free_ char **cmdline = NULL; + int r; + + assert(key_path); + + r = find_executable("ssh-keygen", &ssh_keygen); + if (r < 0) + return log_error_errno(r, "Failed to find ssh-keygen: %m"); + + cmdline = strv_new(ssh_keygen, "-f", key_path, /* don't encrypt the key */ "-N", ""); + if (!cmdline) + return log_oom(); + + if (key_type) { + r = strv_extend_many(&cmdline, "-t", key_type); + if (r < 0) + return log_oom(); + } + + if (DEBUG_LOGGING) { + _cleanup_free_ char *joined = quote_command_line(cmdline, SHELL_ESCAPE_EMPTY); + if (!joined) + return log_oom(); + + log_debug("Executing: %s", joined); + } + + r = safe_fork( + ssh_keygen, + FORK_WAIT|FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE|FORK_REARRANGE_STDIO, + NULL); + if (r < 0) + return r; + if (r == 0) { + execv(ssh_keygen, cmdline); + log_error_errno(errno, "Failed to execve %s: %m", ssh_keygen); + _exit(EXIT_FAILURE); + } + + return 0; +} static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) { _cleanup_(ovmf_config_freep) OvmfConfig *ovmf_config = NULL; _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_free_ char *machine = NULL, *qemu_binary = NULL, *mem = NULL, *trans_scope = NULL, *kernel = NULL; + _cleanup_(rm_rf_physical_and_freep) char *ssh_private_key_path = NULL, *ssh_public_key_path = NULL; _cleanup_close_ int notify_sock_fd = -EBADF; _cleanup_strv_free_ char **cmdline = NULL; _cleanup_free_ int *pass_fds = NULL; @@ -1683,6 +1756,46 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) { return r; } + if (arg_pass_ssh_key) { + _cleanup_free_ char *scope_prefix = NULL, *privkey_path = NULL, *pubkey_path = NULL; + const char *key_type = arg_ssh_key_type ?: "ed25519"; + + r = unit_name_to_prefix(trans_scope, &scope_prefix); + if (r < 0) + return log_error_errno(r, "Failed to strip .scope suffix from scope: %m"); + + privkey_path = strjoin(arg_runtime_directory, "/", scope_prefix, "-", key_type); + if (!privkey_path) + return log_oom(); + + pubkey_path = strjoin(privkey_path, ".pub"); + if (!pubkey_path) + return log_oom(); + + r = generate_ssh_keypair(privkey_path, key_type); + if (r < 0) + return r; + + ssh_private_key_path = TAKE_PTR(privkey_path); + ssh_public_key_path = TAKE_PTR(pubkey_path); + } + + if (ssh_public_key_path && ssh_private_key_path) { + _cleanup_free_ char *scope_prefix = NULL, *cred_path = NULL; + + cred_path = strjoin("ssh.ephemeral-authorized_keys-all:", ssh_public_key_path); + if (!cred_path) + return log_oom(); + + r = machine_credential_load(&arg_credentials, cred_path); + if (r < 0) + return log_error_errno(r, "Failed to load credential %s: %m", cred_path); + + r = unit_name_to_prefix(trans_scope, &scope_prefix); + if (r < 0) + return log_error_errno(r, "Failed to strip .scope suffix from scope: %m"); + } + if (ARCHITECTURE_SUPPORTS_SMBIOS) FOREACH_ARRAY(cred, arg_credentials.credentials, arg_credentials.n_credentials) { _cleanup_free_ char *cred_data_b64 = NULL; |