Pushing Your Profile and SSH Keys

When ever you start supporting a new environment especially in a large corporation usually you are confronted with many systems.  Security will take care of setting up your access across whatever platforms there may be.  But generally you are left holding the bag with setting up your ssh keys and any profile customizations not to mention distribution of any scripts or tools you have come to rely upon.  Of course before you put any tools on a system there are several things to consider.  You definitely want to consider the environments you are first performing the distributions on and it is always good to start with development or lab environments and move out from there.  Also you will need to consider the corporate policies related to the environment which might limit your ability to even have your own set of tools and scripts.  You may be limited down to simple .profile changes and ssh keys.  Implementing a script to push these keys and profiles out may need to go through various degrees of red tape.  Whatever policies and requirements exist in your organization are your responsibility to know and to determine how or if the tools discussed here may be used.

First you should have run ssh-keygen to create your keys and you should have created an .ssh/authorized_keys file in which you have placed your public key.  The next order of business is to create a script that may be copied to any other system to retrieve your profiles from a designated home or administrative system.  This script may be easily created using korn shell.  An example getprof script is inserted below.  The script uses ssh and file handles to stream tar through a ssh connection.  The reason for this is that I have run into organizations that have disabled scp as well as incompatibilities between the versions of ssh on various platforms.  Streaming tar through ssh has been the most effective means of distribution.

#!/bin/ksh
HST=${HST:-AdminHost}
USR=${USR:-UserName}
RCP=${RCP:-“scp -p”}
RSH=${RSH:-“ssh -l”}
SRC=${SRC:-“$USR@$HST”}
## Get Tools
mkdir -p ~/tools/bin
mkdir -p ~/tools/sbin
mkdir -p ~/tools/rbin
mkdir -p ~/tools/dba
mkdir -p ~/tools/etc
mkdir -p ~/tools/man
mkdir -p ~/tools/source
mkdir -p ~/tools/depot
mkdir -p ~/.ssh
## Get ALL
exec 3>all.tar
${RSH} ${USR} ${HST} ‘tar cf – ./.ssh ./.ssh2 .profile .kshrc .hosts .exrc .screenrc tools/bin tools/sbin tools/etc tools/dba tools/man tools/rbin’ >&3
exec 3>&- ; tar xvf all.tar && rm all.tar

But this is only half the battle.  The next problem is to get this script distributed to all the systems you support and to execute it so that your profile is retrieved from a central system to all the systems you will need to support.  To manually do this you would have to login to each system and scp this script to that system, then execute it and answer all the ssh prompts.  A better way is to use a perl script and the Expect.pm module from the central system to automate the login, copy and execution of this script.  You will first need to go to www.cpan.org and get the Expect and IO-Tty perl modules and install them on your systems as most native OS perl distributions do not have these modules.  Instructions for perl module installation may also be found on cpan.

Start your perl script out with a good header for documentation and load your modules. Also setup any global variables in this section.

#!/usr/bin/perl
#
# Header Name: ~/pushprof.pl
#
# Purpose:
#
# Expected Paramters:
#
# Variables:
#
# Tech/Func Leads:
#
# Target Dependencies:
#
# SH Version: $Header$
#
# History:
# 07/11/00 {Your Name} — Script created, Func: None
#
#—————————————————————–
# ————————————-
#| Use/Include Modules |
# ————————————-
use File::Basename;
use Sys::Hostname;
use Shell qw(uname);
use Expect;
use Net::Ping;
# ————————————-
#| Define Header Global Variables |
# ————————————-
## Determine Current Hosts Name and OS
$THISHOST = hostname() ;
$THISOS = uname(“-s”) ;
chomp $THISOS ;
$OS = lc(substr($THISOS,0, 2)) ; ## lower case first two chars
### Global Variables
use vars qw/ %opt /;
$shell_prompt = qr/[\$\#>][>:\s]*\r?$/;

### Local Variables
my ($rc) ;

## Other Script Variables

Then create a section for functions and procedures and it is good practice to have a cleanup procedure and command line processing procedures.  This will give you a good foundation for any other perl scripts you may write in a future and is a template that I have found very useful.

# ————————————-
#| Define Script Functions/Procedures |
# ————————————-
#
# Trap signals and clean up
$Interrupted = 0; # to ensure it has a value
sub CleanUp()
{
syswrite(STDERR, “ouch\n”, 5);
die “\n”;
}
$SIG{INT} = \&CleanUp;
$SIG{TERM} = \&CleanUp;
$SIG{QUIT} = \&CleanUp;
#$SIG{EXIT} = \&CleanUp;
#
# Command line options processing
#
sub Init()
{
my $opt_string = ‘hs:u:’;
use Getopt::Std;
getopts( “$opt_string”, \%opt ) or Usage();
Usage() if $opt{h};
if ( $opt{s} ) {
$host = $opt{s} ;
} else {
Usage();
};

if ( $opt{u} ) {
$username= $opt{u} ;
} else {
Usage();
};

}
sub Usage()
{
print STDERR ”
Syntax: $0 [-h] | [-s {host}] ( -u {username} )
-h Help
-s {hostname} Hostname
-u {username} User Name
” ;
exit;
}

We will also need a special subroutine to handle any login prompts that may be encountered.  The expect object, timeout, passphrase and password will have to be passed to this subroutine so that it can reference the expect methods.

sub LoginHandler
{
my $exp = shift;
my $timeout = shift;
my $passphrase = shift;
my $password = shift;
$spawn_ok = 0;
$exp->expect($timeout*2,
[
qr'(yes\/no)’,
sub {
$spawn_ok = 1;
my $fh = shift;
$fh->send(“yes\n”);
exp_continue_timeout;
}
],
[
‘-re’, qr'(login: $)’,
sub {
$spawn_ok = 1;
my $fh = shift;
$fh->send(“$username\n”);
exp_continue_timeout;
}
],
[
qr/try again/i,
sub {
$spawn_ok = 1;
my $fh = shift;
print $fh “^[\n”;
print STDERR “\nSent Break…\n”; ## don’t continue to avoid lock out
die “ERROR: password not accepted exiting to avoid lockout!\n”;
}
],
[
qr/password.*:\s*\r?$/i,
sub {
$spawn_ok = 1;
my $fh = shift;
print $fh “$password\n”;
exp_continue_timeout;
}
],
[
qr/passphrase/i,
sub {
$spawn_ok = 1;
my $fh = shift;
print $fh “$passphrase\n”;
exp_continue_timeout;
}
],
[
‘-re’, qr/\#\s*\#\s*\#\s*\r?$/i, ## match banner and just continue
sub {
exp_continue_timeout;
}
],
[
eof =>
sub {
if ($spawn_ok) {
die “ERROR: premature EOF in login.\n”;
} else {
die “ERROR: could not spawn telnet.\n”;
}
}
],
‘-re’, $shell_prompt, #’ wait for shell prompt, then exit expect
); ## First expect handle login steps
}

The next step is to code the main subroutine.  I create a main subroutine not because one has to but because I first learned coding in ansi C and I am used to having a main function/subroutine.  I feel that it is also adds a certain level of neatness and form to the code.

# ——————————–
#| Define Main Routine |
# ——————————–
sub Main()
{
# Get Command Line Options
Init();

# Local Variables
my ($rc ); $rc=0;
my $RT=0;

my $password = ‘Password‘;
my $passphrase = ‘PhasePhrase‘;
my $username = ‘UserName‘ if (defined($username));
my $adminhost = ‘AdminHost‘;
my $homedir = “\/export\/home\/$username”;
my $shell_prompt = qr/[\$\#]\s*$/;
my $login = “ssh $username\@$host”;
my $timeout = 120 ;

print STDERR “$login\n” ;

## Test if the host can be ping’d
my $p = Net::Ping->new();
if ( $p->ping($host) ) {
print “Deploying profile to $host \n”;
} else {
print “Seems $host is not reachable \n”;
}
$p->close();

## Start Expect instance and open ssh
my $exp = Expect->spawn($login)
or die “Can’t login to $HOST as $USER:$!\n”;
$exp->log_file(“./pushprof.log”);

## Handle login prompts
LoginHandler($exp, $timeout, $passphrase, $password);

## scp getprof from AdminHost
$exp->send(“scp $username\@$adminhost:$homedir/getprof .\n”);
LoginHandler($exp, $timeout, $passphrase, $password);

## execute getprof
$exp->send(“sh getprof \n”);
LoginHandler($exp, $timeout, $passphrase, $password);

$exp->send(“exit\n”);
$exp->soft_close();
$exp->log_file(undef);

return $rc
} ;

# ——————————–
#| End of Main Routine |
# ——————————–
$rc = Main();
exit $rc

Now we have a script which we can call to push our profile out to any system.  If you want to pass the profile out to more than one system simply put the list into a .hosts file and then use a fore loop calling the pushprof.pl. eg:

for host in `cat .hosts`
do
./pushprof.pl –s $host –u YourUserName
done

About Last Fiddle
I have always had many interests; technology, science, philosophy, theology, politics, history, etc... Currently, life for the past twelve years has placed me in the area of technology fulfilling roles in System Administration and Architecture. But I have always been involved in the local church and enjoy researching and discussing issues of theology, philosophy, history and politics...

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: