Richard W.M. Jones
2015-Aug-27 15:10 UTC
[Libguestfs] [PATCH v2 0/4] p2v: Wait for network to come online before testing connection
Fixes https://bugzilla.redhat.com/1256222
Richard W.M. Jones
2015-Aug-27 15:10 UTC
[Libguestfs] [PATCH v2 1/4] p2v: Wait for network to come online before testing connection (RHBZ#1256222).
When using the virt-p2v ISO in command line mode, we did not wait for the network to come online before starting virt-p2v. Therefore virt-p2v could exit with an error when testing the ssh connection (or on the other hand, it might work randomly). If the user logs in and runs 'launch-virt-p2v' by hand, then it would usually work because the network had been brought online in the meantime. Fix this by waiting for NetworkManager to bring the connection online before calling test_connection(). Note that the obvious way to fix this (changing the systemd service to wait for network-online.target) does *not* work - I added a comment to the service about this. Thanks: Tingting Zheng --- p2v/gui.c | 3 +++ p2v/kernel.c | 1 + p2v/p2v.h | 1 + p2v/p2v.service | 5 +++++ p2v/utils.c | 22 ++++++++++++++++++++++ 5 files changed, 32 insertions(+) diff --git a/p2v/gui.c b/p2v/gui.c index 10d29e2..b3ad05d 100644 --- a/p2v/gui.c +++ b/p2v/gui.c @@ -324,8 +324,11 @@ test_connection_thread (void *data) _("Testing the connection to the conversion server ...")); gtk_spinner_start (GTK_SPINNER (spinner)); gdk_threads_leave (); + + wait_network_online (copy); r = test_connection (copy); free_config (copy); + gdk_threads_enter (); gtk_spinner_stop (GTK_SPINNER (spinner)); diff --git a/p2v/kernel.c b/p2v/kernel.c index b283417..9fec47f 100644 --- a/p2v/kernel.c +++ b/p2v/kernel.c @@ -81,6 +81,7 @@ kernel_configuration (struct config *config, char **cmdline, int cmdline_source) */ p = get_cmdline_key (cmdline, "p2v.skip_test_connection"); if (!p) { + wait_network_online (config); if (test_connection (config) == -1) { const char *err = get_ssh_error (); diff --git a/p2v/p2v.h b/p2v/p2v.h index c9cd653..34f6bcf 100644 --- a/p2v/p2v.h +++ b/p2v/p2v.h @@ -119,6 +119,7 @@ extern const char *get_ssh_error (void); /* utils.c */ extern char *get_if_addr (const char *if_name); extern char *get_if_vendor (const char *if_name, int truncate); +extern void wait_network_online (const struct config *); /* virt-v2v version and features (read from remote). */ extern int v2v_major; diff --git a/p2v/p2v.service b/p2v/p2v.service index a6b5e25..a6c4cd9 100644 --- a/p2v/p2v.service +++ b/p2v/p2v.service @@ -20,6 +20,11 @@ [Unit] Description=p2v service +# For the GUI, we cannot necessarily wait for the network to come +# online, since that may require the "Configure Network" dialog. For +# the command line, we would like the network to be online, but we +# test that within virt-p2v itself. Therefore use network.target +# here, not network-online.target. After=network.target [Service] diff --git a/p2v/utils.c b/p2v/utils.c index 0b30be3..3781a8d 100644 --- a/p2v/utils.c +++ b/p2v/utils.c @@ -26,6 +26,8 @@ #include <locale.h> #include <libintl.h> +#include "ignore-value.h" + #include "p2v.h" #define CHOMP(line,len) \ @@ -134,3 +136,23 @@ get_if_vendor (const char *if_name, int truncate) free (line); return NULL; } + +/* Wait for the network to come online, but don't error out if that + * fails. The caller will call test_connection immediately after this + * which will fail if the network didn't come online. + */ + +/* XXX We could make this configurable. */ +#define NETWORK_ONLINE_COMMAND "nm-online -t 30" + +void +wait_network_online (const struct config *config) +{ + if (config->verbose) { + printf ("waiting for the network to come online ...\n"); + printf ("%s\n", NETWORK_ONLINE_COMMAND); + fflush (stdout); + } + + ignore_value (system (NETWORK_ONLINE_COMMAND)); +} -- 2.5.0
Richard W.M. Jones
2015-Aug-27 15:10 UTC
[Libguestfs] [PATCH v2 2/4] Revert "p2v: Add tip about the root password of the virt-v2v conversion server appliance."
This reverts commit 35efaf820870e7498aacc5d4aaa8c952c635b0e0. --- p2v/gui.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/p2v/gui.c b/p2v/gui.c index b3ad05d..9719e70 100644 --- a/p2v/gui.c +++ b/p2v/gui.c @@ -107,7 +107,6 @@ create_connection_dialog (struct config *config) GtkWidget *port_label; GtkWidget *username_label; GtkWidget *password_label; - GtkWidget *password_tip_label; GtkWidget *test_hbox, *test; GtkWidget *about; GtkWidget *configure_network; @@ -122,7 +121,7 @@ create_connection_dialog (struct config *config) gtk_label_set_line_wrap (GTK_LABEL (intro), TRUE); gtk_misc_set_padding (GTK_MISC (intro), 10, 10); - table = gtk_table_new (6, 2, FALSE); + table = gtk_table_new (5, 2, FALSE); server_label = gtk_label_new (_("Conversion server:")); gtk_misc_set_alignment (GTK_MISC (server_label), 1., 0.5); gtk_table_attach (GTK_TABLE (table), server_label, @@ -171,19 +170,12 @@ create_connection_dialog (struct config *config) gtk_table_attach (GTK_TABLE (table), password_entry, 1, 2, 3, 4, GTK_FILL, GTK_FILL, 4, 4); - password_tip_label = gtk_label_new (NULL); - gtk_label_set_markup (GTK_LABEL (password_tip_label), - _("<i>Tip: If you are using the RHEL virt-v2v conversion server virtual appliance, then the root password is</i> <tt>v2v</tt>")); - gtk_label_set_line_wrap (GTK_LABEL (password_tip_label), TRUE); - gtk_table_attach (GTK_TABLE (table), password_tip_label, - 1, 2, 4, 5, GTK_FILL, GTK_FILL, 4, 4); - sudo_button gtk_check_button_new_with_label (_("Use sudo when running virt-v2v")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sudo_button), config->sudo); gtk_table_attach (GTK_TABLE (table), sudo_button, - 1, 2, 5, 6, GTK_FILL, GTK_FILL, 4, 4); + 1, 2, 4, 5, GTK_FILL, GTK_FILL, 4, 4); test_hbox = gtk_hbox_new (FALSE, 0); test = gtk_button_new_with_label (_("Test connection")); -- 2.5.0
Richard W.M. Jones
2015-Aug-27 15:10 UTC
[Libguestfs] [PATCH v2 3/4] p2v: Add SSH Identity URL.
Allow SSH identities (ie. secret keys) to be used for authentication instead of passwords. --- p2v/config.c | 8 +++ p2v/dependencies.m4 | 4 ++ p2v/gui.c | 34 +++++++++- p2v/kernel.c | 7 +++ p2v/p2v.h | 3 + p2v/p2v.ks.in | 13 +++- p2v/ssh.c | 135 ++++++++++++++++++++++++++++++++++++++-- p2v/virt-p2v-make-disk.in | 9 ++- p2v/virt-p2v-make-disk.pod | 39 ++++++++++++ p2v/virt-p2v-make-kickstart.in | 13 +++- p2v/virt-p2v-make-kickstart.pod | 44 +++++++++++++ p2v/virt-p2v.pod | 100 +++++++++++++++++++++++++++-- 12 files changed, 392 insertions(+), 17 deletions(-) diff --git a/p2v/config.c b/p2v/config.c index 66cafb5..ae8af38 100644 --- a/p2v/config.c +++ b/p2v/config.c @@ -64,6 +64,10 @@ copy_config (struct config *old) c->username = strdup (c->username); if (c->password) c->password = strdup (c->password); + if (c->identity_url) + c->identity_url = strdup (c->identity_url); + if (c->identity_file) + c->identity_file = strdup (c->identity_file); if (c->guestname) c->guestname = strdup (c->guestname); if (c->disks) @@ -92,6 +96,8 @@ free_config (struct config *c) free (c->server); free (c->username); free (c->password); + free (c->identity_url); + free (c->identity_file); free (c->guestname); guestfs_int_free_string_list (c->disks); guestfs_int_free_string_list (c->removable); @@ -122,6 +128,8 @@ print_config (struct config *config, FILE *fp) config->username ? config->username : "none"); fprintf (fp, "password . . . . %s\n", config->password && strlen (config->password) > 0 ? "***" : "none"); + fprintf (fp, "identity URL . . %s\n", + config->identity_url ? config->identity_url : "none"); fprintf (fp, "sudo . . . . . . %s\n", config->sudo ? "true" : "false"); fprintf (fp, "guest name . . . %s\n", diff --git a/p2v/dependencies.m4 b/p2v/dependencies.m4 index e342ce3..acca78e 100644 --- a/p2v/dependencies.m4 +++ b/p2v/dependencies.m4 @@ -28,6 +28,7 @@ ifelse(REDHAT,1, dnl Run as external programs by the p2v binary. /usr/bin/ssh /usr/bin/qemu-nbd + curl dnl The hwdata package contains PCI IDs, used by virt-p2v to display dnl network vendor information (RHBZ#855059). @@ -56,6 +57,7 @@ ifelse(DEBIAN,1, libgtk2.0-0 openssh-client qemu-utils + curl hwdata xorg xserver-xorg-video-all @@ -72,6 +74,7 @@ ifelse(ARCHLINUX,1, gtk2 openssh qemu + curl hwdata xorg-xinit xorg-server @@ -89,6 +92,7 @@ ifelse(SUSE,1, gtk2 /usr/bin/ssh /usr/bin/qemu-nbd + curl hwdata /usr/bin/xinit /usr/bin/Xorg diff --git a/p2v/gui.c b/p2v/gui.c index 9719e70..0339e4f 100644 --- a/p2v/gui.c +++ b/p2v/gui.c @@ -53,7 +53,7 @@ static void set_info_label (void); /* The connection dialog. */ static GtkWidget *conn_dlg, *server_entry, *port_entry, - *username_entry, *password_entry, *sudo_button, + *username_entry, *password_entry, *identity_entry, *sudo_button, *spinner_hbox, *spinner, *spinner_message, *next_button; /* The conversion dialog. */ @@ -107,6 +107,8 @@ create_connection_dialog (struct config *config) GtkWidget *port_label; GtkWidget *username_label; GtkWidget *password_label; + GtkWidget *identity_label; + GtkWidget *identity_tip_label; GtkWidget *test_hbox, *test; GtkWidget *about; GtkWidget *configure_network; @@ -121,7 +123,7 @@ create_connection_dialog (struct config *config) gtk_label_set_line_wrap (GTK_LABEL (intro), TRUE); gtk_misc_set_padding (GTK_MISC (intro), 10, 10); - table = gtk_table_new (5, 2, FALSE); + table = gtk_table_new (7, 2, FALSE); server_label = gtk_label_new (_("Conversion server:")); gtk_misc_set_alignment (GTK_MISC (server_label), 1., 0.5); gtk_table_attach (GTK_TABLE (table), server_label, @@ -170,12 +172,29 @@ create_connection_dialog (struct config *config) gtk_table_attach (GTK_TABLE (table), password_entry, 1, 2, 3, 4, GTK_FILL, GTK_FILL, 4, 4); + identity_label = gtk_label_new (_("SSH Identity URL:")); + gtk_misc_set_alignment (GTK_MISC (identity_label), 1., 0.5); + gtk_table_attach (GTK_TABLE (table), identity_label, + 0, 1, 4, 5, GTK_FILL, GTK_FILL, 4, 4); + identity_entry = gtk_entry_new (); + if (config->identity_url != NULL) + gtk_entry_set_text (GTK_ENTRY (identity_entry), config->identity_url); + gtk_table_attach (GTK_TABLE (table), identity_entry, + 1, 2, 4, 5, GTK_FILL, GTK_FILL, 4, 4); + + identity_tip_label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (identity_tip_label), + _("<i>If using password authentication, leave the SSH Identity URL blank</i>")); + gtk_label_set_line_wrap (GTK_LABEL (identity_tip_label), TRUE); + gtk_table_attach (GTK_TABLE (table), identity_tip_label, + 1, 2, 5, 6, GTK_FILL, GTK_FILL, 4, 4); + sudo_button gtk_check_button_new_with_label (_("Use sudo when running virt-v2v")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sudo_button), config->sudo); gtk_table_attach (GTK_TABLE (table), sudo_button, - 1, 2, 4, 5, GTK_FILL, GTK_FILL, 4, 4); + 1, 2, 6, 7, GTK_FILL, GTK_FILL, 4, 4); test_hbox = gtk_hbox_new (FALSE, 0); test = gtk_button_new_with_label (_("Test connection")); @@ -242,6 +261,7 @@ test_connection_clicked (GtkWidget *w, gpointer data) { struct config *config = data; const gchar *port_str; + const gchar *identity_str; size_t errors = 0; struct config *copy; int err; @@ -279,6 +299,14 @@ test_connection_clicked (GtkWidget *w, gpointer data) free (config->password); config->password = strdup (gtk_entry_get_text (GTK_ENTRY (password_entry))); + free (config->identity_url); + identity_str = gtk_entry_get_text (GTK_ENTRY (identity_entry)); + if (identity_str && STRNEQ (identity_str, "")) + config->identity_url = strdup (identity_str); + else + config->identity_url = NULL; + config->identity_file_needs_update = 1; + config->sudo = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (sudo_button)); if (errors) diff --git a/p2v/kernel.c b/p2v/kernel.c index 9fec47f..4605561 100644 --- a/p2v/kernel.c +++ b/p2v/kernel.c @@ -72,6 +72,13 @@ kernel_configuration (struct config *config, char **cmdline, int cmdline_source) config->password = strdup (p); } + p = get_cmdline_key (cmdline, "p2v.identity"); + if (p) { + free (config->identity_url); + config->identity_url = strdup (p); + config->identity_file_needs_update = 1; + } + p = get_cmdline_key (cmdline, "p2v.sudo"); if (p) config->sudo = 1; diff --git a/p2v/p2v.h b/p2v/p2v.h index 34f6bcf..35b3f3c 100644 --- a/p2v/p2v.h +++ b/p2v/p2v.h @@ -59,6 +59,9 @@ struct config { int port; char *username; char *password; + char *identity_url; + char *identity_file; /* Used to cache the downloaded identity_url. */ + int identity_file_needs_update; int sudo; char *guestname; int vcpus; diff --git a/p2v/p2v.ks.in b/p2v/p2v.ks.in index 4f90af1..cbb0932 100644 --- a/p2v/p2v.ks.in +++ b/p2v/p2v.ks.in @@ -1,5 +1,5 @@ # Kickstart file for creating the virt-p2v ISO. -# (C) Copyright 2014 Red Hat Inc. +# (C) Copyright 2014-2015 Red Hat Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -79,6 +79,17 @@ __DEPENDENCIES__ %post +# Base64-decoding of SSH Identity. + +base64 -d > /var/tmp/id_rsa << EOF +__BASE64_SSH_IDENTITY__ +EOF +if test -s /var/tmp/id_rsa; then + chmod 0600 /var/tmp/id_rsa +else + rm /var/tmp/id_rsa +fi + # Base64-decoding of /etc/issue base64 -d > /etc/issue << EOF diff --git a/p2v/ssh.c b/p2v/ssh.c index afb0a25..3c8d309 100644 --- a/p2v/ssh.c +++ b/p2v/ssh.c @@ -165,8 +165,111 @@ free_regexps (void) pcre_free (portfwd_re); } +/* Download URL to local file using the external 'curl' command. */ +static int +curl_download (const char *url, const char *local_file) +{ + char curl_config_file[] = "/tmp/curl.XXXXXX"; + int fd, r; + size_t i, len; + FILE *fp; + CLEANUP_FREE char *curl_cmd = NULL; + + /* Use a secure curl config file because escaping is easier. */ + fd = mkstemp (curl_config_file); + if (fd == -1) { + perror ("mkstemp"); + exit (EXIT_FAILURE); + } + fp = fdopen (fd, "w"); + if (fp == NULL) { + perror ("fdopen"); + exit (EXIT_FAILURE); + } + fprintf (fp, "url = \""); + len = strlen (url); + for (i = 0; i < len; ++i) { + switch (url[i]) { + case '\\': fprintf (fp, "\\\\"); break; + case '"': fprintf (fp, "\\\""); break; + case '\t': fprintf (fp, "\\t"); break; + case '\n': fprintf (fp, "\\n"); break; + case '\r': fprintf (fp, "\\r"); break; + case '\v': fprintf (fp, "\\v"); break; + default: fputc (url[i], fp); + } + } + fprintf (fp, "\"\n"); + fclose (fp); + + /* Run curl to download the URL to a file. */ + if (asprintf (&curl_cmd, "curl -f -o %s -K %s", + local_file, curl_config_file) == -1) { + perror ("asprintf"); + exit (EXIT_FAILURE); + } + + r = system (curl_cmd); + /* unlink (curl_config_file); - useful for debugging */ + if (r == -1) { + perror ("system"); + exit (EXIT_FAILURE); + } + + /* Did curl subprocess fail? */ + if (WIFEXITED (r) && WEXITSTATUS (r) != 0) { + /* XXX Better error handling. The codes can be looked up in + * the curl(1) man page. + */ + set_ssh_error ("%s: curl error %d", url, WEXITSTATUS (r)); + return -1; + } + else if (!WIFEXITED (r)) { + set_ssh_error ("curl subprocess got a signal (%d)", r); + return -1; + } + + return 0; +} + +/* Re-cache the identity_url if needed. */ +static int +cache_ssh_identity (struct config *config) +{ + int fd; + + /* If it doesn't need downloading, return. */ + if (config->identity_url == NULL || + !config->identity_file_needs_update) + return 0; + + /* Generate a random filename. */ + free (config->identity_file); + config->identity_file = strdup ("/tmp/id.XXXXXX"); + if (config->identity_file == NULL) { + perror ("strdup"); + exit (EXIT_FAILURE); + } + fd = mkstemp (config->identity_file); + if (fd == -1) { + perror ("mkstemp"); + exit (EXIT_FAILURE); + } + close (fd); + + /* Curl download URL to file. */ + if (curl_download (config->identity_url, config->identity_file) == -1) { + free (config->identity_file); + config->identity_file = NULL; + config->identity_file_needs_update = 1; + return -1; + } + + return 0; +} + /* Start ssh subprocess with the standard arguments and possibly some - * optional arguments. Also handles password authentication. + * optional arguments. Also handles authentication. */ static mexp_h * start_ssh (struct config *config, char **extra_args, int wait_prompt) @@ -178,18 +281,29 @@ start_ssh (struct config *config, char **extra_args, int wait_prompt) const int ovecsize = 12; int ovector[ovecsize]; int saved_timeout; + int using_password_auth; + + if (cache_ssh_identity (config) == -1) + return NULL; + + /* Are we using password or identity authentication? */ + using_password_auth = config->identity_file == NULL; /* Create the ssh argument array. */ nr_args = 0; if (extra_args != NULL) nr_args = guestfs_int_count_strings (extra_args); - nr_args += 11; + if (using_password_auth) + nr_args += 11; + else + nr_args += 13; args = malloc (sizeof (char *) * nr_args); if (args == NULL) { perror ("malloc"); exit (EXIT_FAILURE); } + j = 0; args[j++] = "ssh"; args[j++] = "-p"; /* Port. */ @@ -199,8 +313,18 @@ start_ssh (struct config *config, char **extra_args, int wait_prompt) args[j++] = config->username ? config->username : "root"; args[j++] = "-o"; /* Host key will always be novel. */ args[j++] = "StrictHostKeyChecking=no"; - args[j++] = "-o"; /* Only use password authentication. */ - args[j++] = "PreferredAuthentications=keyboard-interactive,password"; + if (using_password_auth) { + /* Only use password authentication. */ + args[j++] = "-o"; + args[j++] = "PreferredAuthentications=keyboard-interactive,password"; + } + else { + /* Use identity file (private key). */ + args[j++] = "-o"; + args[j++] = "PreferredAuthentications=publickey"; + args[j++] = "-i"; + args[j++] = config->identity_file; + } if (extra_args != NULL) { for (i = 0; extra_args[i] != NULL; ++i) args[j++] = extra_args[i]; @@ -213,7 +337,8 @@ start_ssh (struct config *config, char **extra_args, int wait_prompt) if (h == NULL) return NULL; - if (config->password && strlen (config->password) > 0) { + if (using_password_auth && + config->password && strlen (config->password) > 0) { /* Wait for the password prompt. */ switch (mexp_expect (h, (mexp_regexp[]) { diff --git a/p2v/virt-p2v-make-disk.in b/p2v/virt-p2v-make-disk.in index 24a3f44..e58d20e 100644 --- a/p2v/virt-p2v-make-disk.in +++ b/p2v/virt-p2v-make-disk.in @@ -1,7 +1,7 @@ #!/bin/bash - # @configure_input@ # virt-p2v-make-disk -# Copyright (C) 2014 Red Hat Inc. +# Copyright (C) 2014-2015 Red Hat Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -24,7 +24,7 @@ version="@PACKAGE_VERSION@" TEMP=`getopt \ -o o:V \ - --long help,output:,version \ + --long help,inject-ssh-identity:,output:,version \ -n $program -- "$@"` if [ $? != 0 ]; then echo "$program: problem parsing the command line arguments" @@ -33,6 +33,7 @@ fi eval set -- "$TEMP" output+upload usage () { @@ -45,6 +46,9 @@ usage () while true; do case "$1" in + --inject-ssh-identity) + upload="--upload $2:/var/tmp/id_rsa" + shift 2;; -o|--output) output="$2" shift 2;; @@ -171,6 +175,7 @@ virt-builder "$osversion" \ --edit '/etc/systemd/logind.conf: s/^[Login]/[Login]\nReserveVT=1\n/ ' \ + $upload \ $extra_args # We have to do this so the cleanup() handler runs. diff --git a/p2v/virt-p2v-make-disk.pod b/p2v/virt-p2v-make-disk.pod index 2d4b6a5..76cff92 100644 --- a/p2v/virt-p2v-make-disk.pod +++ b/p2v/virt-p2v-make-disk.pod @@ -46,6 +46,40 @@ Write a virt-p2v bootable virtual disk image, and boot it under qemu: where F</var/tmp/guest.img> would be the disk image of some guest that you want to convert (for testing only). +=head1 ADDING AN SSH IDENTITY + +You can inject an SSH identity (private key) file to the image using +the I<--inject-ssh-identity> option. + +First create a key pair. It must have an empty passphrase: + + ssh-keygen -t rsa -N '' -f id_rsa + +This creates a private key (C<id_rsa>) and a public key +(C<id_rsa.pub>) pair. The public key should be appended to the +C<authorized_keys> file on the virt-v2v conversion server (usually to +C</root/.ssh/authorized_keys>). + +The private key should be injected into the disk image and then +discarded: + + virt-p2v-make-disk [...] --inject-ssh-identity id_rsa + rm id_rsa + +When booting virt-p2v, specify the URL of the injected file like this: + + │ User name: [root_____________________________] │ + │ │ + │ Password: [ <leave this field blank> ] │ + │ │ + │ SSH Identity URL: [file:///var/tmp/id_rsa___________] │ + +or if using the kernel command line, add: + + p2v.identity=file:///var/tmp/id_rsa + +For more information, see L<virt-p2v(1)/SSH IDENTITIES>. + =head1 OPTIONS =over 4 @@ -54,6 +88,11 @@ you want to convert (for testing only). Display help. +=item B<--inject-ssh-identity> id_rsa + +Add an SSH identity (private key) file into the image. +See L</ADDING AN SSH IDENTITY> above. + =item B<-o> OUTPUT =item B<--output> OUTPUT diff --git a/p2v/virt-p2v-make-kickstart.in b/p2v/virt-p2v-make-kickstart.in index 14e2865..74bee61 100644 --- a/p2v/virt-p2v-make-kickstart.in +++ b/p2v/virt-p2v-make-kickstart.in @@ -24,7 +24,7 @@ version="@PACKAGE_VERSION@" TEMP=`getopt \ -o o:V \ - --long help,output:,proxy:,version \ + --long help,inject-ssh-identity:,output:,proxy:,version \ -n $program -- "$@"` if [ $? != 0 ]; then echo "$program: problem parsing the command line arguments" @@ -43,9 +43,13 @@ usage () output=p2v.ks proxy+ssh_identity while true; do case "$1" in + --inject-ssh-identity) + ssh_identity="$2" + shift 2;; -o|--output) output="$2" shift 2;; @@ -93,6 +97,11 @@ fi base64_issue="$(base64 $datadir/issue)" base64_launch_virt_p2v="$(base64 $datadir/launch-virt-p2v)" base64_p2v_service="$(base64 $datadir/p2v.service)" +if [ -n "$ssh_identity" ]; then + base64_ssh_identity="$(base64 $ssh_identity)" +else + base64_ssh_identity+fi # virt-p2v binary is too large unless we strip it and compress it. tmpfile="$(mktemp -u)" @@ -169,6 +178,7 @@ done < $depsfile -v "base64_issue=$base64_issue" \ -v "base64_launch_virt_p2v=$base64_launch_virt_p2v" \ -v "base64_p2v_service=$base64_p2v_service" \ + -v "base64_ssh_identity=$base64_ssh_identity" \ -v "base64_virt_p2v=$base64_virt_p2v" \ -v "dependencies=$dependencies" \ -v "md5sum_virt_p2v=$md5sum_virt_p2v" \ @@ -181,6 +191,7 @@ done < $depsfile gsub (/__BASE64_ISSUE__/, base64_issue); gsub (/__BASE64_LAUNCH_VIRT_P2V__/, base64_launch_virt_p2v); gsub (/__BASE64_P2V_SERVICE__/, base64_p2v_service); + gsub (/__BASE64_SSH_IDENTITY__/, base64_ssh_identity); gsub (/__BASE64_VIRT_P2V__/, base64_virt_p2v); gsub (/__DEPENDENCIES__/, dependencies); gsub (/__MD5SUM_VIRT_P2V__/, md5sum_virt_p2v); diff --git a/p2v/virt-p2v-make-kickstart.pod b/p2v/virt-p2v-make-kickstart.pod index d833a45..fad0397 100644 --- a/p2v/virt-p2v-make-kickstart.pod +++ b/p2v/virt-p2v-make-kickstart.pod @@ -191,6 +191,45 @@ pxelinux starts up. =back +=head1 ADDING AN SSH IDENTITY + +You can inject an SSH identity (private key) file to the kickstart and +hence into the ISO using the I<--inject-ssh-identity> option. Note +that you I<cannot> inject a key once the ISO has been built. + +First create a key pair. It must have an empty passphrase: + + ssh-keygen -t rsa -N '' -f id_rsa + +This creates a private key (C<id_rsa>) and a public key +(C<id_rsa.pub>) pair. The public key should be appended to the +C<authorized_keys> file on the virt-v2v conversion server (usually to +C</root/.ssh/authorized_keys>). + +The private key should be added to the kickstart file and then +discarded: + + virt-p2v-make-kickstart [...] --inject-ssh-identity id_rsa + rm id_rsa + +The ISO can then be built from the kickstart in the usual way (see +above), and it will contain the embedded SSH identity +(F</var/tmp/id_rsa>). + +When booting virt-p2v, specify the URL of the injected file like this: + + │ User name: [root_____________________________] │ + │ │ + │ Password: [ <leave this field blank> ] │ + │ │ + │ SSH Identity URL: [file:///var/tmp/id_rsa___________] │ + +or if using the kernel command line, add: + + p2v.identity=file:///var/tmp/id_rsa + +For more information, see L<virt-p2v(1)/SSH IDENTITIES>. + =head1 OPTIONS =over 4 @@ -199,6 +238,11 @@ pxelinux starts up. Display help. +=item B<--inject-ssh-identity> id_rsa + +Add an SSH identity (private key) file into the kickstart. +See L</ADDING AN SSH IDENTITY> above. + =item B<-o> OUTPUT =item B<--output> OUTPUT diff --git a/p2v/virt-p2v.pod b/p2v/virt-p2v.pod index e37e654..be6b015 100644 --- a/p2v/virt-p2v.pod +++ b/p2v/virt-p2v.pod @@ -89,10 +89,13 @@ When virt-p2v starts up in GUI mode, the first dialog looks like this: │ │ │ Password: [_________________________________] │ │ │ + │ SSH Identity URL: [_________________________________] │ + │ │ -In the fields above, you must enter the hostname, SSH port number, -remote user name and password of the conversion server. The -conversion server must have an up to date version of virt-v2v. +In the fields above, you must enter the details of the conversion +server: the hostname, SSH port number, remote user name, and either +the password or SSH identity (private key) URL. The conversion server +must have an up to date version of virt-v2v. Normally you must log in to the conversion server as root, but if you check the following box: @@ -320,8 +323,17 @@ The default is to try with no password. If this fails then virt-p2v will ask the user to type the password (probably several times during conversion). -Note that virt-p2v does not support authentication using key -distribution at this time. +This setting is ignored if C<p2v.identity> is present. + +=item B<p2v.identity=URL> + +Provide a URL pointing to an SSH identity (private key) file. The URL +is interpreted by L<curl(1)> so any URL that curl supports can be used +here, including C<https://> and C<file://>. For more information on +using SSH identities, see L</SSH IDENTITIES> below. + +If C<p2v.identity> is present, it overrides C<p2v.password>. There is +no fallback. =item B<p2v.sudo> @@ -474,6 +486,84 @@ Set up a static IPv4 network configuration. =back +=head1 SSH IDENTITIES + +As a somewhat more secure alternative to password authentication, you +can use an SSH identity (private key) for authentication. + +First create a key pair. It must have an empty passphrase: + + ssh-keygen -t rsa -N '' -f id_rsa + +This creates a private key (C<id_rsa>) and a public key +(C<id_rsa.pub>) pair. + +The public key should be appended to the C<authorized_keys> file on +the virt-v2v conversion server (usually to +C</root/.ssh/authorized_keys>). + +For distributing the private key, there are four scenarios from least +secure to most secure: + +=over 4 + +=item 1. + +Not using SSH identities at all, ie. password authentication. + +Anyone who can sniff the PXE boot parameters from the network or +observe the password some other way can log in to the virt-v2v +conversion server. + +=item 2. + +SSH identity embedded in the virt-p2v ISO or disk image. In the GUI, use: + + │ Password: [ <leave this field blank> ] │ + │ │ + │ SSH Identity URL: [file:///var/tmp/id_rsa_____________] │ + +or on the kernel command line: + + p2v.identity=file:///var/tmp/id_rsa + +The SSH private key can still be sniffed from the network if using +standard PXE. + +=item 3. + +SSH identity downloaded from a website. In the GUI, use: + + │ Password: [ <leave this field blank> ] │ + │ │ + │ SSH Identity URL: [https://internal.example.com/id_rsa] │ + +or on the kernel command line: + + p2v.identity=https://internal.example.com/id_rsa + +Anyone could still download the private key and use it to log in to +the virt-v2v conversion server, but you could provide some extra +security by configuring the web server to only allow connections from +P2V machines. + +=item 4. + +SSH identity embedded in the virt-p2v ISO or disk image (like 2.), +I<and> use of secure PXE, PXE over separate physical network, or +sneakernet to distribute virt-p2v to the physical machine. + +=back + +Both L<virt-p2v-make-disk(1)> and L<virt-p2v-make-kickstart(1)> have +the same option I<--inject-ssh-identity> for injecting the private key +into the virt-p2v disk image / ISO. See also the following manual +sections: + +L<virt-p2v-make-disk(1)/ADDING AN SSH IDENTITY> + +L<virt-p2v-make-kickstart(1)/ADDING AN SSH IDENTITY> + =head1 OPTIONS =over 4 -- 2.5.0
Richard W.M. Jones
2015-Aug-27 15:10 UTC
[Libguestfs] [PATCH v2 4/4] p2v: Add a test for the PXE boot path (RHBZ#1256222).
Build the P2V disk image and boot it. We don't actually use PXE specifically, but we do test the whole PXE / kernel command line path much more thoroughly. This is a 'check-slow' test because it takes ages to run. --- .gitignore | 11 ++++ TODO | 8 +++ p2v/Makefile.am | 68 +++++++++++++++++++- p2v/test-virt-p2v-pxe.sh | 120 +++++++++++++++++++++++++++++++++++ p2v/test-virt-p2v-pxe.sshd_config.in | 39 ++++++++++++ 5 files changed, 245 insertions(+), 1 deletion(-) create mode 100755 p2v/test-virt-p2v-pxe.sh create mode 100644 p2v/test-virt-p2v-pxe.sshd_config.in diff --git a/.gitignore b/.gitignore index fb972a7..e502018 100644 --- a/.gitignore +++ b/.gitignore @@ -345,9 +345,20 @@ Makefile.in /p2v/dependencies.redhat /p2v/dependencies.suse /p2v/launch-virt-p2v +/p2v/stamp-test-virt-p2v-pxe-hostkey +/p2v/stamp-test-virt-p2v-pxe-kernel +/p2v/stamp-test-virt-p2v-pxe-userkey /p2v/stamp-virt-p2v.pod /p2v/stamp-virt-p2v-make-disk.pod /p2v/stamp-virt-p2v-make-kickstart.pod +/p2v/test-virt-p2v-pxe.id_rsa +/p2v/test-virt-p2v-pxe.id_rsa.pub +/p2v/test-virt-p2v-pxe.img +/p2v/test-virt-p2v-pxe.initramfs +/p2v/test-virt-p2v-pxe.sshd_config +/p2v/test-virt-p2v-pxe.ssh_host_rsa_key +/p2v/test-virt-p2v-pxe.ssh_host_rsa_key.pub +/p2v/test-virt-p2v-pxe.vmlinuz /p2v/virt-p2v /p2v/virt-p2v.1 /p2v/virt-p2v-make-disk diff --git a/TODO b/TODO index 666400e..2b865b5 100644 --- a/TODO +++ b/TODO @@ -576,3 +576,11 @@ Subsecond handling in virt-diff, virt-ls Handle nanoseconds properly. You should be able to specify them on the command line and display them. + +virt-p2v +-------- + +virt-p2v-make-disk and virt-p2v-make-kickstart should have +options to let you inject SSH identities, eg: + + virt-p2v-make-disk [...] --inject-ssh-identity=id_rsa diff --git a/p2v/Makefile.am b/p2v/Makefile.am index 57efd8d..c66a887 100644 --- a/p2v/Makefile.am +++ b/p2v/Makefile.am @@ -163,6 +163,72 @@ TESTS += \ test-virt-p2v.sh endif ENABLE_APPLIANCE +SLOW_TESTS = \ + test-virt-p2v-pxe.sh + +check-slow: test-virt-p2v-pxe.img \ + test-virt-p2v-pxe.vmlinuz test-virt-p2v-pxe.initramfs \ + test-virt-p2v-pxe.sshd_config \ + test-virt-p2v-pxe.ssh_host_rsa_key \ + test-virt-p2v-pxe.ssh_host_rsa_key.pub \ + test-virt-p2v-pxe.id_rsa test-virt-p2v-pxe.id_rsa.pub + $(MAKE) check TESTS="$(SLOW_TESTS)" + +test-virt-p2v-pxe.img: virt-p2v-make-disk \ + virt-p2v \ + test-virt-p2v-pxe.id_rsa + $(top_builddir)/run virt-p2v-make-disk \ + --inject-ssh-identity=test-virt-p2v-pxe.id_rsa \ + -o $@-t \ + fedora-22 + mv $@-t $@ + +test-virt-p2v-pxe.vmlinuz test-virt-p2v-pxe.initramfs: stamp-test-virt-p2v-pxe-kernel + +stamp-test-virt-p2v-pxe-kernel: test-virt-p2v-pxe.img + rm -f $@ vmlinuz initramfs test-virt-p2v-pxe.vmlinuz test-virt-p2v-pxe.initramfs + $(top_builddir)/run virt-get-kernel --unversioned-names -a $< + mv vmlinuz test-virt-p2v-pxe.vmlinuz + mv initramfs test-virt-p2v-pxe.initramfs + touch $@ + +test-virt-p2v-pxe.sshd_config: test-virt-p2v-pxe.sshd_config.in + rm -f $@ $@-t + @AWK@ \ + -v "abs_builddir=$(abs_builddir)" \ + '{ \ + gsub (/__RANDOM_PORT__/, 10000 + int (1000 * rand())); \ + gsub (/__abs_builddir__/, abs_builddir); \ + print; \ + }' < $< > $@-t + chmod 0444 $@-t + mv $@-t $@ + +test-virt-p2v-pxe.ssh_host_rsa_key test-virt-p2v-pxe.ssh_host_rsa_key.pub: stamp-test-virt-p2v-pxe-hostkey + +stamp-test-virt-p2v-pxe-hostkey: + rm -f test-virt-p2v-pxe.ssh_host_rsa_key + rm -f test-virt-p2v-pxe.ssh_host_rsa_key.pub + ssh-keygen -t rsa -f test-virt-p2v-pxe.ssh_host_rsa_key -N '' + touch $@ + +test-virt-p2v-pxe.id_rsa test-virt-p2v-pxe.id_rsa.pub: stamp-test-virt-p2v-pxe-userkey + +stamp-test-virt-p2v-pxe-userkey: + rm -f test-virt-p2v-pxe.id_rsa + rm -f test-virt-p2v-pxe.id_rsa.pub + ssh-keygen -t rsa -f test-virt-p2v-pxe.id_rsa -N '' + touch $@ + +# Don't clean ssh_host_rsa_key{,.pub} or id_rsa{,.pub} since those +# consume system entropy to regenerate. +CLEANFILES += \ + stamp-test-virt-p2v-pxe-kernel \ + test-virt-p2v-pxe.img \ + test-virt-p2v-pxe.vmlinuz \ + test-virt-p2v-pxe.initramfs \ + test-virt-p2v-pxe.sshd_config + EXTRA_DIST += \ - $(TESTS) \ + $(TESTS) $(SLOW_TESTS) \ test-virt-p2v-ssh.sh diff --git a/p2v/test-virt-p2v-pxe.sh b/p2v/test-virt-p2v-pxe.sh new file mode 100755 index 0000000..f05cb96 --- /dev/null +++ b/p2v/test-virt-p2v-pxe.sh @@ -0,0 +1,120 @@ +#!/bin/bash - +# libguestfs virt-p2v test script +# Copyright (C) 2014-2015 Red Hat Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +# Test virt-p2v in non-GUI mode with something resembling the +# PXE boot code path. This tests: +# * virt-p2v-make-disk +# * systemd p2v.service +# * launch-virt-p2v +# * networking +# * virt-p2v in kernel command-line mode + +unset CDPATH +export LANG=C +set -e + +if [ -n "$SKIP_TEST_VIRT_P2V_PXE_SH" ]; then + echo "$0: test skipped because environment variable is set" + exit 77 +fi + +if [ "$(guestfish get-backend)" = "uml" ]; then + echo "$0: test skipped because UML backend does not support network" + exit 77 +fi + +if [ "$(uname -m)" != "x86_64" ]; then + echo "$0: test skipped because !x86_64" + exit 77 +fi + +qemu=qemu-system-x86_64 +if ! $qemu -help >/dev/null 2>&1; then + echo "$0: test skipped because $qemu not found" + exit 77 +fi + +img="test-virt-p2v-pxe.img" +if ! test -f $img; then + echo "$0: test skipped because $img was not created" + exit 77 +fi + +guestsdir="$(cd ../tests/guests && pwd)" +f="$guestsdir/windows.img" +if ! test -f $f; then + echo "$0: test skipped because phony Windows image was not created" + exit 77 +fi + +virt_tools_data_dir=${VIRT_TOOLS_DATA_DIR:-/usr/share/virt-tools} +if ! test -r $virt_tools_data_dir/rhsrvany.exe; then + echo "$0: test skipped because rhsrvany.exe is not installed" + exit 77 +fi + +d=test-virt-p2v-pxe.d +rm -rf $d +mkdir $d + +# Start the ssh server. Kill it if the script exits for any reason. +# Note you must use an absolute path to exec sshd. +`which sshd` -f test-virt-p2v-pxe.sshd_config -D & +sshd_pid=$! +cleanup () +{ + kill $sshd_pid +} +trap cleanup INT QUIT TERM EXIT ERR + +# Get the randomly assigned sshd port number. +port="$(grep ^Port test-virt-p2v-pxe.sshd_config | awk '{print $2}')" + +# Connect as the local user. +username="$(id -un)" + +# Output storage path. +os="$(cd $d; pwd)" + +# The Linux kernel command line. +cmdline="root=/dev/sda3 ro console=ttyS0 printk.time=1 p2v.debug p2v.server=10.0.2.2 p2v.port=$port p2v.username=$username p2v.identity=file:///var/tmp/id_rsa p2v.name=windows p2v.o=local p2v.os=$os" + +# Run virt-p2v inside qemu. +$qemu \ + -nodefconfig \ + -display none \ + -machine accel=kvm:tcg \ + -m 2048 \ + -kernel test-virt-p2v-pxe.vmlinuz \ + -initrd test-virt-p2v-pxe.initramfs \ + -append "$cmdline" \ + -boot c \ + -device virtio-scsi-pci,id=scsi \ + -drive file=$img,format=raw,snapshot=on,if=none,index=0,id=hd0 \ + -device scsi-hd,drive=hd0 \ + -drive file=$f,format=raw,snapshot=on,if=none,index=1,id=hd1 \ + -device scsi-hd,drive=hd1 \ + -netdev user,id=usernet \ + -device virtio-net-pci,netdev=usernet \ + -serial stdio + +# Test the libvirt XML metadata and a disk was created. +test -f $d/windows.xml +test -f $d/windows-sda + +rm -r $d diff --git a/p2v/test-virt-p2v-pxe.sshd_config.in b/p2v/test-virt-p2v-pxe.sshd_config.in new file mode 100644 index 0000000..d8b6203 --- /dev/null +++ b/p2v/test-virt-p2v-pxe.sshd_config.in @@ -0,0 +1,39 @@ +# libguestfs virt-p2v test script +# @configure_input@ +# Copyright (C) 2014-2015 Red Hat Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +# Minimal sshd_config used by test-virt-p2v-pxe.ssh when it runs +# a captive sshd. + +# Choose a random high port number. +Port __RANDOM_PORT__ + +# Only allow connections from loopback. +ListenAddress [::1] +ListenAddress 127.0.0.1 + +# Privilege separation breaks non-root usage of sshd. +UsePrivilegeSeparation no + +# Use local files instead of inaccessible global configuration. +PidFile __abs_builddir__/test-virt-p2v-pxe.sshd.pid +HostKey __abs_builddir__/test-virt-p2v-pxe.ssh_host_rsa_key + +AuthorizedKeysFile __abs_builddir__/test-virt-p2v-pxe.id_rsa.pub + +# Don't check file permissions. +StrictModes no -- 2.5.0
Seemingly Similar Threads
- [PATCH 0/4] Various p2v fixes and features
- [PATCH 0/2] p2v: Allow virt-p2v to be built with Gtk 2 or 3.
- [PATCH v2 0/3] p2v: Allow virt-p2v to be built with Gtk 2 or 3.
- [PATCH v3] p2v: Allow virt-p2v to be built with Gtk 2 or 3.
- [PATCH 0/2] p2v: Warn if vcpus or memory would be larger than supported by RHEL (RHBZ#823758).