Richard W.M. Jones
2015-Aug-25 18:34 UTC
[Libguestfs] [PATCH 0/4] Various p2v fixes and features
A mixed bag, but all the patches make sense together! Patch 1: Fix a bug that Tingting found: https://bugzilla.redhat.com/show_bug.cgi?id=1256222 Patch 2: Revert a patch that makes no sense now that we've added virt-v2v into base RHEL. This is just included because it's a cleanup needed before applying patch 3. Patch 3: Add the ability to use SSH identities (private keys) for virt-p2v to authenticate with the conversion server (as a possibly more secure alternative to passwords). Patch 4: Test the bug fixed in patch 1, which involves for various convoluted reasons using SSH identities. Rich.
Richard W.M. Jones
2015-Aug-25 18:34 UTC
[Libguestfs] [PATCH 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-25 18:34 UTC
[Libguestfs] [PATCH 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-25 18:34 UTC
[Libguestfs] [PATCH 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 | 12 +++-
p2v/ssh.c | 135 ++++++++++++++++++++++++++++++++++++++--
p2v/virt-p2v-make-disk.pod | 33 ++++++++++
p2v/virt-p2v-make-kickstart.pod | 35 +++++++++++
p2v/virt-p2v.pod | 98 +++++++++++++++++++++++++++--
10 files changed, 355 insertions(+), 14 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..d9ad98f 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,16 @@ __DEPENDENCIES__
%post
+# Inject an SSH Identity by uncommenting the following lines and
+# pasting the contents between the 'cat' and 'EOF':
+
+#cat > /var/tmp/id_rsa << EOF
+#-----BEGIN RSA PRIVATE KEY-----
+# ...
+#-----END RSA PRIVATE KEY-----
+#EOF
+#chmod 0600 /var/tmp/id_rsa
+
# 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.pod b/p2v/virt-p2v-make-disk.pod
index 2d4b6a5..30c4ca4 100644
--- a/p2v/virt-p2v-make-disk.pod
+++ b/p2v/virt-p2v-make-disk.pod
@@ -46,6 +46,39 @@ 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
+L<guestfish(1)>. 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:
+
+ guestfish -a p2v.img -i upload id_rsa /var/tmp/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
diff --git a/p2v/virt-p2v-make-kickstart.pod b/p2v/virt-p2v-make-kickstart.pod
index d833a45..bbf566c 100644
--- a/p2v/virt-p2v-make-kickstart.pod
+++ b/p2v/virt-p2v-make-kickstart.pod
@@ -191,6 +191,41 @@ pxelinux starts up.
=back
+=head1 ADDING AN SSH IDENTITY
+
+You can inject an SSH identity (private key) file to the ISO by
+modifying the kickstart. 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 by editing it
+and searching for the string C<SSH Identity> and following the
+instructions there. 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
diff --git a/p2v/virt-p2v.pod b/p2v/virt-p2v.pod
index e37e654..93fe4ab 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,82 @@ 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
+
+For instructions on how to embed the private key in the virt-p2v ISO
+or disk image, see 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-25 18:34 UTC
[Libguestfs] [PATCH 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 | 67 ++++++++++++++++++-
p2v/test-virt-p2v-pxe.sh | 120 +++++++++++++++++++++++++++++++++++
p2v/test-virt-p2v-pxe.sshd_config.in | 39 ++++++++++++
5 files changed, 244 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..0652674 100644
--- a/p2v/Makefile.am
+++ b/p2v/Makefile.am
@@ -163,6 +163,71 @@ 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 -o $@-t fedora-22
+ $(top_builddir)/run guestfish -a $@-t -i \
+ upload test-virt-p2v-pxe.id_rsa /var/tmp/id_rsa
+ 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
Reasonably Related Threads
- [PATCH v2 0/4] p2v: Wait for network to come online before testing connection
- [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).