diff --git a/perl-xCAT/xCAT/RemoteShellExp.pm b/perl-xCAT/xCAT/RemoteShellExp.pm index 925465dc0..90b15ed43 100755 --- a/perl-xCAT/xCAT/RemoteShellExp.pm +++ b/perl-xCAT/xCAT/RemoteShellExp.pm @@ -35,7 +35,7 @@ package xCAT::RemoteShellExp; node. DSH_TO_USERID - The userid on the node where the ssh keys will be updated. DSH_ENABLE_SSH - Node to node root passwordless ssh will be setup. - + DSH_ZONE_SSHKEYS - directory containing the zones root .ssh keys Usage: remoteshellexp [-t node list] test ssh connection to the node @@ -144,7 +144,7 @@ sub remoteshellexp } else { $from_userid="root"; } - # set User on the node where we will send the keys + # set User on the node where we will send the keys # this id can be a local id as well as root if ($ENV{'DSH_TO_USERID'}) { $to_userid=$ENV{'DSH_TO_USERID'}; @@ -154,18 +154,19 @@ sub remoteshellexp # set User home directory to find the ssh public key to send # For non-root ids information may not be in /etc/passwd # but elsewhere like LDAP + if ($ENV{'DSH_FROM_USERID_HOME'}) { - $home=$ENV{'DSH_FROM_USERID_HOME'}; + $home=$ENV{'DSH_FROM_USERID_HOME'}; } else { - $home=xCAT::Utils->getHomeDir($from_userid); + $home=xCAT::Utils->getHomeDir($from_userid); } - # This indicates we will generate new ssh keys for the user, # if they are not already there - my $key="$home/.ssh/id_rsa"; - my $key2="$home/.ssh/id_rsa.pub"; - # Check to see if empty - if (-z $key) { + # unless using zones + my $key="$home/.ssh/id_rsa"; + my $key2="$home/.ssh/id_rsa.pub"; + # Check to see if empty + if (-z $key) { my $rsp = {}; $rsp->{error}->[0] = "The $key file is empty. Remove it and rerun the command."; @@ -183,8 +184,7 @@ sub remoteshellexp } if (($flag eq "k") && (!(-e $key))) { - # if the file size of the id_rsa key is 0, tell them to remove it - # and run the command again + # updating keys and the key file does not exist $rc=xCAT::RemoteShellExp->gensshkeys($expecttimeout); } # send ssh keys to the nodes/devices, to setup passwordless ssh @@ -200,6 +200,9 @@ sub remoteshellexp if ($ssh_setup_cmd) { # setup ssh on devices $rc=xCAT::RemoteShellExp->senddeviceskeys($remoteshell,$remotecopy,$to_userid,$to_user_password,$home,$ssh_setup_cmd,$nodes, $expecttimeout); } else { #setup ssh on nodes + if ($ENV{'DSH_ZONE_SSHKEYS'}) { # if using zones the override the location of the keys + $home= $ENV{'DSH_ZONE_SSHKEYS'}; + } $rc=xCAT::RemoteShellExp->sendnodeskeys($remoteshell,$remotecopy,$to_userid,$to_user_password,$home,$nodes, $expecttimeout); } } @@ -496,11 +499,14 @@ sub sendnodeskeys # copy to the node to the temp directory # scp $HOME/.ssh/tmp/authorized_keys to_userid@:/tmp/$to_userid/.ssh # scp $HOME/.ssh/id_rsa.pub to_userid@:/tmp/$to_userid/.ssh + # Note if using zones, the keys do not come from ~/.ssh but from the + # zone table, sshkeydir attribute. For zones the userid is always root # If you are going to enable ssh to ssh between nodes, then # scp $HOME/.ssh/id_rsa to that temp directory on the node # copy the script $HOME/.ssh/copy.sh to the node, it will do the # the work of setting up the user's ssh keys and clean up # ssh (run) copy.sh on the node + my @nodelist=split(/,/,$nodes); foreach my $node (@nodelist) { $sendkeys = new Expect; diff --git a/perl-xCAT/xCAT/TableUtils.pm b/perl-xCAT/xCAT/TableUtils.pm index faec9af36..d1538b454 100644 --- a/perl-xCAT/xCAT/TableUtils.pm +++ b/perl-xCAT/xCAT/TableUtils.pm @@ -427,23 +427,28 @@ rmdir \"/tmp/$to_userid\" \n"; if (($from_userid eq "root") && (!($ENV{'DEVICETYPE'}))) { # Need to check if nodes are in a zone. my @zones; - my $tab = xCAT::Table->new("zone"); + my $tab = xCAT::Table->new("zone"); + my @zones; if ($tab) { # if we have zones, need to send the zone keys to each node in the zone - my @zones = $tab->getAllAttribs('zonename'); + my @attribs = ("zonename"); + @zones = $tab->getAllAttribs(@attribs); $tab->close(); } else { $rsp->{data}->[0] = "Could not open zone table.\n"; xCAT::MsgUtils->message("E", $rsp, $::CALLBACK); return 1; } + # check for zones, key send is different if zones defined or not + if (@zones) { # we have zones defined my $rc = xCAT::TableUtils->sendkeysTOzones($ref_nodes,$expecttimeout); if ($rc != 0) { $rsp->{data}->[0] = "Error sending ssh keys to the zones.\n"; xCAT::MsgUtils->message("E", $rsp, $::CALLBACK); + exit 1; } } else { # no zones @@ -614,26 +619,117 @@ sub sendkeysNOzones #-------------------------------------------------------------------------------- sub sendkeysTOzones { - my ($class, $ref_nodes,$expecttimeout) = @_; + my ($class, $ref_nodes,$expecttimeout) = @_; my @nodes=$ref_nodes; my $n_str = $nodes[0]; - my @nodelist= split(",", $n_str); - my %zonehash =xCAT::Zone->getNodeZones(@nodelist); - # for each zone in the zonehash - # if sshbetweennodes is yes - # $ENV{'DSH_ENABLE_SSH'} = "YES"; - # else - # unset $ENV{'DSH_ENABLE_SSH'} - # send the keys to the nodes - # my $rc=xCAT::RemoteShellExp->remoteshellexp("s",$::CALLBACK,"/usr/bin/ssh",$nodelist,$expecttimeout); - # if ($rc != 0) - # { - # $rsp->{data}->[0] = "remoteshellexp failed sending keys to $zonename."; - # xCAT::MsgUtils->message("E", $rsp, $::CALLBACK); - # - # } - # } # endforeach - return 0; + my @nodes= split(",", $n_str); + my $rsp = (); + my $cmd; + my $roothome = xCAT::Utils->getHomeDir("root"); + my $zonehash =xCAT::Zone->getzoneinfo($::CALLBACK,\@nodes); + foreach my $zonename (keys %$zonehash) { + # build list of nodes + my $zonenodelist=""; + foreach my $node (@{$zonehash->{$zonename}->{nodes}}) { + $zonenodelist .= $node; + $zonenodelist .= ","; + + } + $zonenodelist =~ s/,$//; # remove last comma + # if any nodes defined for the zone + if ($zonenodelist) { + # check to see if we enable passwordless ssh between the nodes + if (!(defined($zonehash->{$zonename}->{sshbetweennodes}))|| + (($zonehash->{$zonename}->{sshbetweennodes} =~ /^yes$/i ) + || ($zonehash->{$zonename}->{sshbetweennodes} eq "1"))) { + + $ENV{'DSH_ENABLE_SSH'} = "YES"; + } else { + delete $ENV{'DSH_ENABLE_SSH'}; # do not enable passwordless ssh + } + # point to the ssh keys to send for this zone + my $keydir = $zonehash->{$zonename}->{sshkeydir} ; + + # check to see if the id_rsa and id_rsa.pub key is in the directory + my $key="$keydir/id_rsa"; + my $key2="$keydir/id_rsa.pub"; + # Check to see if empty + if (!(-e $key)) { + my $rsp = {}; + $rsp->{error}->[0] = + "The $key file does not exist for $zonename. Need to use chzone to regenerate the keys."; + xCAT::MsgUtils->message("E", $rsp, $::CALLBACK, 1); + return 1; + } + if (!(-e $key2)) { + my $rsp = {}; + $rsp->{error}->[0] = + "The $key2 file does not exist for $zonename. Need to use chzone to regenerate the keys."; + xCAT::MsgUtils->message("E", $rsp, $::CALLBACK, 1); + return 1; + + } + + # now put copy.sh in the zone directory from ~/.ssh + my $rootkeydir="$roothome/.ssh"; + if ($rootkeydir ne $keydir) { # the zone keydir is not the same as ~/.ssh. + $cmd="cp $rootkeydir/copy.sh $keydir"; + xCAT::Utils->runcmd($cmd, 0); + if ($::RUNCMD_RC != 0) + { + my $rsp = {}; + $rsp->{error}->[0] = + "Could not copy copy.sh to the zone key dir"; + xCAT::MsgUtils->message("E", $rsp, $::CALLBACK); + return 1; + } + } + # Also create $keydir/tmp and put root's id_rsa.pub (in authorized_keys) for the transfer + $cmd="mkdir -p $keydir/tmp"; + xCAT::Utils->runcmd($cmd, 0); + if ($::RUNCMD_RC != 0) + { + my $rsp = {}; + $rsp->{error}->[0] = + "Could not mkdir the zone $keydir/tmp"; + xCAT::MsgUtils->message("E", $rsp, $::CALLBACK); + return 1; + } + # create authorized_keys file + if (xCAT::Utils->isMN()) { # if on Management Node + $cmd = " cp $roothome/.ssh/id_rsa.pub $keydir/tmp/authorized_keys"; + } else { # SN + $cmd = " cp $roothome/.ssh/authorized_keys $keydir/tmp/authorized_keys"; + } + xCAT::Utils->runcmd($cmd, 0); + if ($::RUNCMD_RC != 0) + { + $rsp->{data}->[0] = "$cmd failed.\n"; + xCAT::MsgUtils->message("E", $rsp, $::CALLBACK); + return (1); + } + else + { + chmod 0600, "$keydir/.ssh/tmp/authorized_keys"; + } + # strip off .ssh + my ($newkeydir,$ssh) = (split(/\.ssh/, $keydir)); + $ENV{'DSH_ZONE_SSHKEYS'} =$newkeydir ; + # send the keys to the nodes + my $rc=xCAT::RemoteShellExp->remoteshellexp("s",$::CALLBACK,"/usr/bin/ssh", + $zonenodelist,$expecttimeout); + if ($rc != 0) + { + $rsp = {}; + $rsp->{data}->[0] = "remoteshellexp failed sending keys to $zonename."; + xCAT::MsgUtils->message("E", $rsp, $::CALLBACK); + + } + } # end nodes in the zone + + } # end for each zone + + return (0); } #-------------------------------------------------------------------------------- diff --git a/perl-xCAT/xCAT/Zone.pm b/perl-xCAT/xCAT/Zone.pm index b9f93c72a..64fa1d5c3 100644 --- a/perl-xCAT/xCAT/Zone.pm +++ b/perl-xCAT/xCAT/Zone.pm @@ -49,7 +49,7 @@ This program module file, is a set of Zone utilities used by xCAT *zone commands =cut #-------------------------------------------------------------------------------- -sub genSSHRootKeys +sub genSSHRootKeys { my ($class, $callback, $keydir,$zonename,$rsakey) = @_; @@ -71,22 +71,6 @@ sub genSSHRootKeys } } - # - # create /install/postscripts/_ssh/zonename if needed - # - my $installdir = xCAT::TableUtils->getInstallDir(); # get installdir - if (!-d "$installdir/postscripts/_ssh/$zonename") - { - my $cmd = "/bin/mkdir -m 755 -p $installdir/postscripts/_ssh/$zonename"; - my $output = xCAT::Utils->runcmd("$cmd", 0); - if ($::RUNCMD_RC != 0) - { - my $rsp = {}; - $rsp->{error}->[0] = "Could not create $installdir/postscripts/_ssh/$zonename directory."; - xCAT::MsgUtils->message("E", $rsp, $callback); - return 1; - } - } #need to gen a new rsa key for root for the zone my $pubfile = "$keydir/id_rsa.pub"; @@ -152,29 +136,6 @@ sub genSSHRootKeys xCAT::MsgUtils->message("E", $rsp, $callback); return 1; } - # copy authorized_keys for install on node - if (-r $pubfile) - { - my $cmd = - "/bin/cp -p $pubfile $installdir/postscripts/_ssh/$zonename "; - my $output = xCAT::Utils->runcmd("$cmd", 0); - if ($::RUNCMD_RC != 0) - { - my $rsp = {}; - $rsp->{error}->[0] = - "Could not copy $pubfile to $installdir/postscripts/_ssh/$zonename"; - xCAT::MsgUtils->message("E", $rsp, $callback); - return 1; - - } - } - else - { - my $rsp = {}; - $rsp->{error}->[0] = - "Could not copy $pubfile to $installdir/postscripts/_ssh/$zonename, because $pubfile does not exist."; - xCAT::MsgUtils->message("E", $rsp, $callback); - } } #-------------------------------------------------------------------------------- @@ -188,7 +149,7 @@ sub genSSHRootKeys =cut #-------------------------------------------------------------------------------- -sub getdefaultzone +sub getdefaultzone { my ($class, $callback) = @_; my $defaultzone; @@ -262,7 +223,7 @@ sub iszonedefined =cut #-------------------------------------------------------------------------------- -sub getzoneinfo +sub getzoneinfo { my ($class, $callback,$nodes) = @_; $::GETZONEINFO_RC=0; @@ -279,6 +240,7 @@ sub getzoneinfo my $zonename=$zone->{zonename}; $zonehash->{$zonename}->{sshkeydir}= $zone->{sshkeydir}; $zonehash->{$zonename}->{defaultzone}= $zone->{defaultzone}; + $zonehash->{$zonename}->{sshbetweennodes}= $zone->{sshbetweennodes}; # find the defaultzone if ((defined($zone->{defaultzone})) && (($zone->{defaultzone} =~ /^yes$/i ) @@ -299,44 +261,40 @@ sub getzoneinfo my $nodelisttab = xCAT::Table->new("nodelist"); my $nodehash = $nodelisttab->getNodesAttribs(\@$nodes, ['zonename']); # for each of the nodes, look up it's zone name and assign to the zonehash - # if the node is a service node, it is assigned to the __xcatzone which gets its keys from - # the ~/.ssh dir no matter what in the database for the zonename. # If the nodes nodelist.zonename attribute is a zonename, it is assigned to that zone # If the nodes nodelist.zonename attribute is undefined: # If there is a defaultzone in the zone table, the node is assigned to that zone - # If there is no defaultzone in the zone table, the node is assigned to the ~.ssh keydir + # If there is no defaultzone error out - my @allSN=xCAT::ServiceNodeUtils->getAllSN("ALL"); # read all the servicenodes define - my $xcatzone = "__xcatzone"; # if node is in no zones or a service node, use this one - $zonehash->{$xcatzone}->{sshkeydir}= "~/.ssh"; foreach my $node (@$nodes) { my $zonename; - if (grep(/^$node$/, @allSN)) { # this is a servicenode, treat special - $zonename=$xcatzone; # always use ~/.ssh directory - } else { # use the nodelist.zonename attribute - $zonename=$nodehash->{$node}->[0]->{zonename}; - } + $zonename=$nodehash->{$node}->[0]->{zonename}; if (defined($zonename)) { # zonename explicitly defined in nodelist.zonename - # check to see if defined in the zone table - if (!(grep(/^$zonename$/, @zones))) { + # check to see if defined in the zone table + unless ( xCAT::Zone->iszonedefined($zonename)) { my $rsp = {}; $rsp->{error}->[0] = "$node has a zonenane: $zonename that is not define in the zone table. Remove the zonename from the node, or create the zone using mkzone."; xCAT::MsgUtils->message("E", $rsp, $callback); $::GETZONEINFO_RC =1; return; - } - push @{$zonehash->{$zonename}->{nodes}},$node; + } + push @{$zonehash->{$zonename}->{nodes}},$node; } else { # no explict zonename if (defined ($defaultzone)) { # there is a default zone in the zone table, use it push @{$zonehash->{$defaultzone}->{nodes}},$node; - } else { # if no default then use the ~/.ssh keys as the default, put them in the __xcatzone - push @{$zonehash->{$xcatzone}->{nodes}},$node; + } else { # if no default, this is an error + my $rsp = {}; + $rsp->{error}->[0] = + "There is no default zone defined in the zone table. There must be exactly one default zone. "; + xCAT::MsgUtils->message("E", $rsp, $callback); + $::GETZONEINFO_RC =1; + return; } } } - return; + return $zonehash; } 1;