[Saga-devel] saga SVN commit 3343: /trunk/adaptors/ssh/

amerzky at cct.lsu.edu amerzky at cct.lsu.edu
Wed Jan 21 16:06:13 CST 2009


User: amerzky
Date: 2009/01/21 04:06 PM

Modified:
 /trunk/adaptors/ssh/ssh_context/
  ssh_context_adaptor.cpp
 /trunk/adaptors/ssh/ssh_job/
  ssh_job.cpp, ssh_job.hpp, ssh_job_service.cpp, ssh_job_service.hpp

Log:
 allow forwarding of ssh keys.  In some sense, this is like proxy
 delegation, only that keys don't expire.  So, please be careful!
 A

File Changes:

Directory: /trunk/adaptors/ssh/ssh_context/
===========================================

File [modified]: ssh_context_adaptor.cpp
Delta lines: +61 -11
===================================================================
--- trunk/adaptors/ssh/ssh_context/ssh_context_adaptor.cpp	2009-01-21 17:19:30 UTC (rev 3342)
+++ trunk/adaptors/ssh/ssh_context/ssh_context_adaptor.cpp	2009-01-21 22:05:50 UTC (rev 3343)
@@ -134,7 +134,6 @@
 
     std::pair <std::string, std::string> entry (saga::attributes::context_type, 
                                                 "ssh");
-
     entries.push_back (entry);
 
     s->add_proto_context (entries);
@@ -151,16 +150,51 @@
 //
 // init a cert, either from a given path, or from a default location
 //
-context_cpi_impl::cert_info_t context_cpi_impl::get_cert_info (std::string path)
+context_cpi_impl::cert_info_t context_cpi_impl::get_cert_info (std::string key_path)
 {
+  std::string key_path_pub;
+  std::string key_user;
+
   cert_info_t ci;
 
-  ci.success = true;
+  ci.success     = true;
+  ci.private_key = key_path;
 
-  // fall back to default if needed
-  if ( path == "" )
+
+  // tr to find fallbacks
+  if ( ci.private_key == "" )
   {
+    // if SAGA_SSH_KEY is set in the environment, we use that as default
+    const char * saga_ssh_key = ::getenv ("SAGA_SSH_KEY");
+
+    if ( NULL != saga_ssh_key )
+    {
+      ci.private_key = saga_ssh_key;
+
+      // is the public key location also given?
+      const char * saga_ssh_pub = ::getenv ("SAGA_SSH_PUB");
+
+      if ( NULL != saga_ssh_pub )
+      {
+        key_path_pub = saga_ssh_pub;
+      }
+
+      // is the public key location also given?
+      const char * saga_ssh_user = ::getenv ("SAGA_SSH_USER");
+
+      if ( NULL != saga_ssh_user )
+      {
+        key_user = saga_ssh_user;
+      }
+    }
+  }
+
+
+  // anther fallback is ~/.{ssh,ssh2}/id_[dr]sa
+  if ( ci.private_key == "" )
+  {
     const char * home = ::getenv ("HOME");
+
     if ( home == NULL )
     {
       ci.errormessage = "Cannot determine home directory, i.e. default ssh key location";
@@ -192,13 +226,13 @@
     if ( d.exists  ("id_dsa") &&
          d.is_file ("id_dsa") )
     {
-      path = d.get_url ().get_path () + "/id_dsa"; 
+      ci.private_key = d.get_url ().get_path () + "/id_dsa"; 
     }
     else
     if ( d.exists  ("id_rsa") &&
          d.is_file ("id_rsa") )
     {
-      path = d.get_url ().get_path () + "/id_rsa"; 
+      ci.private_key = d.get_url ().get_path () + "/id_rsa"; 
     }
     else
     {
@@ -210,14 +244,30 @@
 
 
 
+
+  // default for public key
+  if ( key_path_pub == "" )
+  {
+    ci.public_key  = key_path + ".pub";
+  }
+
+  ci.public_key  = key_path_pub;
+
+
+  // default for key_user
+  if ( key_user == "" )
+  {
+    key_user = ::getpwuid (::getuid ())->pw_name;
+  }
+
+  ci.userid  = key_user;
+
+
+
   // check if given private key, and related public-key, exist
   // FIXME: use saga::filesystem!
   struct stat buf;
 
-  ci.private_key = path;
-  ci.public_key  = path + ".pub";
-  ci.userid      = ::getpwuid (::getuid ())->pw_name;
-
   if ( 0 != ::stat (ci.private_key.c_str (), &buf) )
   {
     ci.errormessage = "Cannot access private ssh key";

Directory: /trunk/adaptors/ssh/ssh_job/
=======================================

File [modified]: ssh_job.cpp
Delta lines: +15 -0
===================================================================
--- trunk/adaptors/ssh/ssh_job/ssh_job.cpp	2009-01-21 17:19:30 UTC (rev 3342)
+++ trunk/adaptors/ssh/ssh_job/ssh_job.cpp	2009-01-21 22:05:50 UTC (rev 3343)
@@ -169,6 +169,21 @@
       new_args.push_back (key_);
       new_args.push_back (user_ + "@" + host_);
 
+      // add environment as ssh, via /usr/bin/env
+      if ( old_jd_.attribute_exists (saga::job::attributes::description_environment) )
+      {
+        std::vector <std::string> env = old_jd_.get_vector_attribute 
+                                          (saga::job::attributes::description_environment);
+        
+        new_args.push_back ("/usr/bin/env");
+
+        for ( unsigned int i = 0; i < env.size (); i++ )
+        {
+          new_args.push_back (env[i]);
+        }
+      }
+
+
       // readd old exe and args
       new_args.push_back (old_exe);
 

File [modified]: ssh_job.hpp
Delta lines: +0 -1
===================================================================
--- trunk/adaptors/ssh/ssh_job/ssh_job.hpp	2009-01-21 17:19:30 UTC (rev 3342)
+++ trunk/adaptors/ssh/ssh_job/ssh_job.hpp	2009-01-21 22:05:50 UTC (rev 3343)
@@ -59,7 +59,6 @@
       std::vector <std::string>            scp_opt_;
 
       std::map <std::string, std::string>  ini_;
-      std::map <std::string, std::string>  env_;
 
       saga::job::job                       j_;        // forward to default job
 

File [modified]: ssh_job_service.cpp
Delta lines: +171 -30
===================================================================
--- trunk/adaptors/ssh/ssh_job/ssh_job_service.cpp	2009-01-21 17:19:30 UTC (rev 3342)
+++ trunk/adaptors/ssh/ssh_job/ssh_job_service.cpp	2009-01-21 22:05:50 UTC (rev 3343)
@@ -7,9 +7,13 @@
 
 // system includes
 #include <pwd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 
+
 // stl includes
 #include <vector>
+#include <fstream>
 
 // saga includes
 #include <saga/saga.hpp>
@@ -46,6 +50,12 @@
     adaptor_data  adata (this);
     instance_data idata (this);
 
+    // create local job service which handles all job submissions.  This
+    // may throw.
+    js_ = TR1::shared_ptr <saga::job::service> (new saga::job::service ("fork://localhost"));
+
+
+
     ini_ = adap_ini.get_section ("preferences").get_entries ();
     rm_  = idata->rm_;
 
@@ -81,7 +91,6 @@
       if ( contexts[i].attribute_exists ("Type") &&
            contexts[i].get_attribute ("Type") == "ssh" )
       {
-        SAGA_LOG_ALWAYS ("Using this context");
         ssh_contexts.push_back (contexts[i]);
       }
     }
@@ -109,6 +118,8 @@
     // pick up that identity
     //
     // FIXME: we need to be able to create multiple default ssh contexts
+    
+    
 
     for ( int j = 0; j < ssh_contexts.size (); j++ )
     {
@@ -119,13 +130,15 @@
 
 
       // FIXME: check if attribs exist
-      if ( ! ctx_.attribute_exists ("UserKey") )
+      if ( ! ctx_.attribute_exists ("UserKey") ||
+           ! ctx_.attribute_exists ("UserCert") )
       {
         // _need_ key to be useful
         break;
       }
 
-      key_  = ctx_.get_attribute ("UserKey");
+      loc_ssh_key_priv_  = ctx_.get_attribute ("UserKey");
+      loc_ssh_key_pub_   = ctx_.get_attribute ("UserCert");
 
 
       if ( ctx_.attribute_exists ("UserKey") )
@@ -150,25 +163,136 @@
       }
 
 
+      // determine some additional vars used for the envoronement of started
+      // jobs
+      // FIXME: the saga jobid leads to invalid command lines and file
+      // names
+      // try
+      // {
+      //   saga::job::job self = js_->get_self ();
+      //   parent_id_ = self.get_job_id ();
+      // }
+      // catch ( ... )
+      {
+        // did not get jobid - invent one
+        std::stringstream ss;
+        ss << "saga-parent-id" << ::getpid ();
+        parent_id_ = ss.str ();
+      }
+
+      rem_ssh_key_pub_  = std::string ("/tmp/saga_") + parent_id_ + "_ssh";
+      rem_ssh_key_priv_ = std::string ("/tmp/saga_") + parent_id_ + "_ssh.pub";
+
+      env_.push_back (std::string ("SAGA_PARENT_JOBID") + "=" + parent_id_);
+      env_.push_back (std::string ("SAGA_SSH_KEY")      + "=" + rem_ssh_key_pub_);
+      env_.push_back (std::string ("SAGA_SSH_PUB")      + "=" + rem_ssh_key_priv_);
+      env_.push_back (std::string ("SAGA_SSH_USER")     + "=" + user_);
+
+
+      // prepare the remote host
       saga::adaptors::utils::process proc;
 
-      if ( ini_["distribute_idendity"] == "yes"  ||
-           ini_["distribute_idendity"] == "true" )
+      if ( ini_["distribute_identity"] == "yes"  ||
+           ini_["distribute_identity"] == "true" )
       {
-        SAGA_LOG_DEBUG (" copying identity file");
+        {
+          SAGA_LOG_DEBUG (" copying private key");
 
-        proc.set_cmd  (scp_bin_);
-        proc.set_args (scp_opt_);
+          proc.set_cmd  (scp_bin_);
+          proc.set_args (scp_opt_);
 
-        // FIXME: ensure that context is complete
-        proc.add_args ("-i", key_);
+          // first copy private key
+          // FIXME: ensure that context is complete
+          proc.add_args ("-i", loc_ssh_key_priv_);
 
-        // file to stage
-        // FIXME: ensure that context is complete
-        // FIXME: we silently assume that the .ssh dirctory exists
-        // FIXME: the target below SHOULD not exist *aehem*
-        proc.add_arg (key_);
-        proc.add_arg (user_ + "@" + host_ + ":.ssh/id_saga");
+          // file to stage
+          // FIXME: ensure that context is complete
+          // FIXME: we silently assume that the .ssh dirctory exists
+          // FIXME: the target below SHOULD not exist *aehem*
+          proc.add_arg (loc_ssh_key_priv_);
+          proc.add_arg (user_ + "@" + host_ + ":" + rem_ssh_key_priv_);
+
+          (void) proc.run_sync ();
+
+          if ( ! proc.done () )
+          {
+            SAGA_ADAPTOR_THROW ("Could not copy private key", saga::NoSuccess);
+          }
+        }
+
+        {
+          SAGA_LOG_DEBUG (" copying public key");
+
+          // if ok, copy public key
+          proc.set_args (scp_opt_);
+
+          // FIXME: ensure that context is complete
+          proc.add_args ("-i", loc_ssh_key_priv_);
+
+          // file to stage
+          // FIXME: ensure that context is complete
+          // FIXME: we silently assume that the .ssh dirctory exists
+          // FIXME: the target below SHOULD not exist *aehem*
+          proc.add_arg (loc_ssh_key_pub_);
+          proc.add_arg (user_ + "@" + host_ + ":" + rem_ssh_key_pub_);
+
+          (void) proc.run_sync ();
+
+          if ( ! proc.done () )
+          {
+            SAGA_ADAPTOR_THROW ("Could not copy public key", saga::NoSuccess);
+          }
+        }
+
+        {
+          SAGA_LOG_DEBUG (" register public key");
+          // FIXME: need to append public key to local authorized_keys
+          // file, so that application can call back home.  A key is
+          // exactly one line: so we append the key, and then do
+          // a sort|uniq on the authorized_keys file, to avoid
+          // duplicates.
+
+          char* home_tmp = ::getenv ("HOME");
+
+          if ( home_tmp == NULL )
+          {
+            SAGA_ADAPTOR_THROW ("Could not determine home directory", saga::NoSuccess);
+          }
+          
+          std::string home = home_tmp;
+
+
+          // FIXME: use better name
+          std::fstream cmd;
+          cmd.open ("/tmp/saga_tmp_cmd", std::fstream::out);
+
+          cmd << " cat " << loc_ssh_key_pub_ 
+              <<         " " << home << "/.ssh/authorized_keys"
+              << " | sort | uniq > /tmp/saga_keys_tmp" 
+              << std::endl
+              << "  mv /tmp/saga_keys_tmp " 
+              << home << "/.ssh/authorized_keys" 
+              << std::endl;
+
+          cmd.close();
+
+          ::chmod ("/tmp/saga_tmp_cmd", S_IRWXU);
+
+          proc.set_cmd     ("/bin/sh");
+          proc.clear_args  ();
+          proc.add_args    ("-c", "/tmp/saga_tmp_cmd");
+
+          (void) proc.run_sync ();
+
+          if ( ! proc.done () )
+          {
+            SAGA_ADAPTOR_THROW ("Could not register public key", saga::NoSuccess);
+          }
+
+          // remove temporary script
+          ::unlink ("/tmp/saga_tmp_cmd");
+
+        }
       }
       else
       {
@@ -178,28 +302,25 @@
         proc.set_args (ssh_opt_);
 
         // FIXME: ensure that context is complete
-        proc.add_args ("-i", key_);
+        proc.add_args ("-i", loc_ssh_key_priv_);
         proc.add_arg  (      user_ + "@" + host_);
         proc.add_arg  ("true");
-      }
 
+        (void) proc.run_sync ();
 
-      (void) proc.run_sync ();
+        if ( ! proc.done () )
+        {
+          SAGA_ADAPTOR_THROW ("Could not run a test ssh command", saga::NoSuccess);
+        }
+      }
 
-      if ( proc.done () )
-      {
-        // remember key location, so we can tell the started jobs about it
-        env_["SAGA_ADAPTOR_SSH_KEY"] = ".ssh/id_saga";
 
-        // create local job service which handles all job submissions
-        js_ = TR1::shared_ptr <saga::job::service> (new saga::job::service ("fork://localhost"));
-
-        // we are done
-        return;
-      }
+      // we are done - no exception 'til now!
+      return;
     }
 
-    // no context was ok for scp or ssh - flag error
+    // no context was ok for scp or ssh, or preparation failed - flag error
+    // FIXME: throw above when error occurs, with better error message
     SAGA_ADAPTOR_THROW ("Could not connect to remote host", saga::NoSuccess);
   }
 
@@ -220,6 +341,26 @@
     saga::session s;
     s.add_context (ctx_);
 
+    // the ssh job adaptor sets a couple of env variables:
+    //   SAGA_PARENT_JOBID: saga job id of self, i.e. the spawning process
+    //   SAGA_SSH_KEY:      location of private ssh key used to spawn process
+    //   SAGA_SSH_PUB:      location of public  ssh key used to spawn process
+    //   SAGA_SSH_USER:     user which spawned the process
+    std::vector <std::string> new_env;
+    if ( jd.attribute_exists (saga::job::attributes::description_environment) )
+    {
+      new_env = jd.get_vector_attribute (saga::job::attributes::description_environment);
+    }
+
+    for ( int i = 0; i < env_.size (); i++ )
+    {
+      new_env.push_back (env_[i]);
+      SAGA_LOG_ALWAYS(env_[i].c_str ());
+    }
+
+    jd.set_vector_attribute (saga::job::attributes::description_environment, new_env);
+
+    // create the job with the 'fixed' job description
     saga::job::job job = saga::adaptors::job (rm_, jd, s);
     ret = job;
   }

File [modified]: ssh_job_service.hpp
Delta lines: +7 -1
===================================================================
--- trunk/adaptors/ssh/ssh_job/ssh_job_service.hpp	2009-01-21 17:19:30 UTC (rev 3342)
+++ trunk/adaptors/ssh/ssh_job/ssh_job_service.hpp	2009-01-21 22:05:50 UTC (rev 3343)
@@ -59,11 +59,17 @@
       std::string                          ssh_bin_;
       std::string                          scp_bin_;
 
+      std::string                          parent_id_;    // id of self
+      std::string                          loc_ssh_key_pub_;  // local ssh key
+      std::string                          loc_ssh_key_priv_; // local ssh key
+      std::string                          rem_ssh_key_pub_;  // deployed ssh key
+      std::string                          rem_ssh_key_priv_; // deployed ssh key
+
       std::vector <std::string>            ssh_opt_;
       std::vector <std::string>            scp_opt_;
 
       std::map <std::string, std::string>  ini_;
-      std::map <std::string, std::string>  env_;
+      std::vector <std::string>            env_;
 
       void dump_context (saga::context c);
 



More information about the saga-devel mailing list