This adds a customize option:

  virt-customize --ssh-inject USER[=KEY]
  virt-builder --ssh-inject USER[=KEY]
  virt-sysprep --ssh-inject USER[=KEY]

In each case this either injects the current (host) user's ssh pubkey
into the guest user USER (adding it to ~USER/.ssh/authorized_keys in
the guest), or you can specify a particular key.

For example:

  virt-builder fedora-20 --ssh-inject root

will add the local user's ssh pubkey into the root account of the
newly created guest.  Or:

  virt-customize -a disk.img \
     --ssh-inject 'mary=ssh-rsa AAAA.... mary@localhost'

adds the given ssh pubkey to mary's account in the guest.

This doesn't set the SELinux labels correctly on newly created files
and directories, so you have to use --selinux-relabel (probably we
should fix this as part of the general effort to fix SELinux
relabelling).  However it should preserve the labels if the
~/.ssh/authorized_keys file already exists.
---
 builder/cmdline.ml         |  4 +--
 customize/customize_run.ml | 81 ++++++++++++++++++++++++++++++++++++++++++++++
 generator/customize.ml     | 18 +++++++++++
 3 files changed, 101 insertions(+), 2 deletions(-)

diff --git a/builder/cmdline.ml b/builder/cmdline.ml
index c0584f7..e21d5bb 100644
--- a/builder/cmdline.ml
+++ b/builder/cmdline.ml
@@ -306,8 +306,8 @@ read the man page virt-builder(1).
           | `Command _ | `InstallPackages _ | `Script _ | `Update -> true
           | `Delete _ | `Edit _ | `FirstbootCommand _ | `FirstbootPackages _
           | `FirstbootScript _ | `Hostname _ | `Link _ | `Mkdir _
-          | `Password _ | `RootPassword _ | `Scrub _ | `Timezone _ | `Upload _
-          | `Write _ | `Chmod _ -> false
+          | `Password _ | `RootPassword _ | `Scrub _ | `SSHInject _
+          | `Timezone _ | `Upload _ | `Write _ | `Chmod _ -> false
         ) ops.ops in
         if requires_execute_on_guest then
           error (f_"sorry, cannot run commands on a guest with a different 
architecture");
diff --git a/customize/customize_run.ml b/customize/customize_run.ml
index 51b218a..099d611 100644
--- a/customize/customize_run.ml
+++ b/customize/customize_run.ml
@@ -135,6 +135,81 @@ exec >>%s 2>&1
       error (f_"sorry, don't know how to use --update with the '%s' package 
manager") pm
   in
 
+  (* Find the local [on the host] user's SSH public key.  See
+   * ssh-copy-id(1) default_ID_file for rationale.
+   *)
+  let pubkey_re = Str.regexp "^id.*\\.pub$" in
+  let pubkey_ignore_re = Str.regexp ".*-cert\\.pub$" in
+
+  let local_user_ssh_pubkey () =
+    let home_dir =
+      try getenv "HOME"
+      with Not_found ->
+        error (f_"ssh-inject: $HOME environment variable is not set") in
+    let ssh_dir = home_dir // ".ssh" in
+    let files = Sys.readdir ssh_dir in
+    let files = Array.to_list files in
+    let files = List.filter (
+      fun file ->
+        Str.string_match pubkey_re file 0 &&
+          not (Str.string_match pubkey_ignore_re file 0)
+    ) files in
+    if files = [] then
+      error (f_"ssh-inject: no public key file found in %s") ssh_dir;
+
+    (* Newest file. *)
+    let files = List.map (
+      fun file ->
+        let file = ssh_dir // file in
+        let stat = stat file in
+        (file, stat.st_mtime)
+    ) files in
+    let files = List.sort (fun (_,m1) (_,m2) -> compare m2 m1) files in
+    let newest_file = fst (List.hd files) in
+
+    (* Read and return the public key. *)
+    let key = read_whole_file newest_file in
+    if key = "" then
+      error (f_"ssh-inject: public key file (%s) is empty") newest_file;
+
+    key
+
+  (* Inject SSH key, where possible. *)
+  and do_ssh_inject username key =
+    match g#inspect_get_type root with
+    | "linux" | "freebsd" | "netbsd" | "openbsd" | "hurd" ->
+      (* If the key doesn't have \n at the end, add it. *)
+      let len = String.length key in
+      if len < 1 then
+        error (f_"ssh-inject: key is an empty string");
+      let key = if key.[len-1] = '\n' then key else key ^ "\n" in
+
+      (* Get user's home directory. *)
+      g#aug_init "/" 0;
+      let expr = sprintf "/files/etc/passwd/%s/home" username in
+      let home_dir = g#aug_get expr in
+      g#aug_close ();
+
+      (* Create ~user/.ssh if it doesn't exist. *)
+      let ssh_dir = sprintf "%s/.ssh" home_dir in
+      if not (g#exists ssh_dir) then (
+        g#mkdir ssh_dir;
+        g#chmod 0o755 ssh_dir
+      );
+
+      (* Create ~user/.ssh/authorized_keys if it doesn't exist. *)
+      let auth_keys = sprintf "%s/authorized_keys" ssh_dir in
+      if not (g#exists auth_keys) then (
+        g#touch auth_keys;
+        g#chmod 0o644 auth_keys
+      );
+
+      (* Append the key. *)
+      g#write_append auth_keys key
+    | typ ->
+      warning (f_"don't know how to inject SSH keys into %s guests") typ
+  in
+
   (* Set the random seed. *)
   msg (f_"Setting a random seed");
   if not (Random_seed.set_random_seed g root) then
@@ -232,6 +307,12 @@ exec >>%s 2>&1
       msg (f_"Scrubbing: %s") path;
       g#scrub_file path
 
+    | `SSHInject user_key ->
+      let user, key = string_split "=" user_key in
+      let key = if key = "" then local_user_ssh_pubkey () else key in
+      msg (f_"SSH key inject: %s") user;
+      do_ssh_inject user key
+
     | `Timezone tz ->
       msg (f_"Setting the timezone: %s") tz;
       if not (Timezone.set_timezone g root tz) then
diff --git a/generator/customize.ml b/generator/customize.ml
index 8642a54..ef91b62 100644
--- a/generator/customize.ml
+++ b/generator/customize.ml
@@ -260,6 +260,24 @@ It cannot delete directories, only regular files.
 =back";
   };
 
+  { op_name = "ssh-inject";
+    op_type = String "USER[=KEY]";
+    op_discrim = "`SSHInject";
+    op_shortdesc = "Inject a public key into the guest";
+    op_pod_longdesc = "\
+Inject an ssh key so the given C<USER> will be able to log in over
+ssh without supplying a password.
+
+If just I<--ssh-inject> C<USER> is given then we look in the I<current>
+user's C<~/.ssh> directory to find the default public ID file.  That
+key is uploaded.
+
+You can also upload a specific key using I<--ssh-inject> C<\"USER=KEY\">
+
+In either case, this will create the C<~USER/.ssh> directory if required,
+and create or append the key to C<~USER/.ssh/authorized_keys>"
+  };
+
   { op_name = "timezone";
     op_type = String "TIMEZONE";
     op_discrim = "`Timezone";
-- 
2.0.4

_______________________________________________
Libguestfs mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/libguestfs

Reply via email to