Como ejemplo escribiremos un guión que realiza una conexión
ssh
con una red protegida por un bastion.
Un bastión es un ordenador que es el único punto de entrada/salida
a internet desde una red interna. Los bastiones se usan para disminuir
los problemas de seguridad al establecer una barrera
entre las zonas públicas y privadas.
Nuestro guión supone que hay un ordenador bastión
el cual permite una conexión ssh
. Una vez validada la
conexión mediante el username
y el password
se nos pregunta a que máquina queremos conectarnos. Suponemos que dependiendo
de la máquina será necesario introducir de nuevo la clave o no.
pp2@nereida:~/src/perl/expect$ cat -n bastion 1 #!/usr/bin/perl -w 2 use strict; 3 use Getopt::Long; 4 use Pod::Usage; 5 use List::MoreUtils qw(any); 6 use Expect; 7 8 my $VERSION = "1.0"; 9 my $RSH = `which ssh`; 10 chomp($RSH); 11 my $password = ""; 12 my $user = $ENV{USER}; 13 my $machine = 'millo'; 14 my $bastion; 15 my $config; 16 17 my $delay = 1; 18 my $passwordprompt = 'word:\s$'; 19 my $machineprompt = 'salir\)(\s)+$'; 20 my @MOREPASSWORDS = (); 21 22 GetOptions( 23 'ssh=s' => \$RSH, 24 'password=s' => \$password, 25 'user=s' => \$user, 26 'machine=s' => \$machine, 27 'bastion=s' => \$bastion, 28 'config=s' => \$config, 29 'version' => \&version, 30 'help' => \&man, 31 ) or croak usage(); 32 33 34 ($config) = @ARGV if @ARGV; 35 36 $config = ".bastion.conf" if !$config and !$bastion and -r ".bastion.conf"; 37 $config = "$ENV{HOME}/.bastion.conf" if !$config and !$bastion and -r "$ENV{HOME}/.bastion.conf"; 38 39 eval_config($config) if $config and -r $config; 40 41 die man() unless $bastion and $bastion =~ m{(\w+\.?)+}; 42 my $host_to_login_to=$user.'@'.$bastion;
El programa comienza obteniendo los valores desde la línea de comandos
o desde un fichero de configuración (opción config
).
31 my ($passwordprompt, $machineprompt, $delay, @MOREPASSWORDS); 32 33 eval_config($config) if $config and -r $config;El fichero de configuración esta escrito en Perl y es evaluado de manera parecida a como se hizo en la práctica 1.9.
Estos son los contenidos de un fichero de configuración:
pp2@nereida:~/src/perl/expect$ cat -n bastion.conf 1 $password = 'password'; 2 $user = 'user'; 3 $machine = 'banot'; 4 #$machine = 'millo'; 5 $bastion = 'exthost.instituto.ull.es'; 6 7 # Seconds to wait before leaving 8 $delay = 3; 9 10 # user@instituto's password: 11 $passwordprompt = 'word:\s$'; 12 13 # Nombre de la máquina?:(q para salir) 14 $machineprompt = 'salir\)(\s)+$'; 15 16 @MOREPASSWORDS = qw(timple manis banot tonga);
42 my $host_to_login_to=$user.'@'.$bastion; 43 44 my $rsh=Expect->spawn($RSH,$host_to_login_to); 45 46 $rsh->expect($delay,'-re', $passwordprompt)||(die"Never got password prompt\n"); 47 print $rsh "$password\r"; 48 49 $rsh->expect($delay,'-re',$machineprompt)||(die"Never got machine prompt\n"); 50 print $rsh "$machine\r"; 51 52 if (any { /^$machine$/ } @MOREPASSWORDS ) { 53 $rsh->expect($delay,'-re', $passwordprompt)||(die"Never got password prompt\n"); 54 print $rsh "$password\r"; 55 } 56 # Retornar control al usuario 57 $rsh->interact(); .. .............. # subrutinas de apoyo
El método spawn ejecuta el comando (línea 37) creando una seudoterminal como vía de comunicación entre el comando y el programa cliente.
Una seudoterminal es un proceso que proporciona un canal de comunicaciones entre los dos procesos implicados mediante la emulación de una terminal de texto. Casi todos los programas admiten una comunicación vía una terminal y alguno (editores, el programa para cambiar la clave de usuario, etc.) solo admiten una comunicación via una seudoterminal. Además, la comunicación con seudoterminales no esta bufferada. Se evita asi el riesgo de bloqueo que introduce la presencia de memorias auxiliares.
Despues de creado el objeto seudoterminal $rsh
se establece un diálogo con el comando lanzado (lineas 39-40, 42-43
y 46-47). El método expect
permite analizar la salida del comando hasta que case con la expresión
regular proporcionada como argumento. La llamada termina si no se
produce el casamiento en menos de $delay
segundos.
Mediante llamadas a print
en el manejador de la seudoterminal
$rsh
proporcionamos
la entrada al comando lanzado.
Si la máquina de la red interna a la que entramos requiere una validación
adicional (esta entre las descritas en el array @MOREPASSWORDS
)
la (misma) clave es introducida de nuevo.
El método interact (línea 50) devuelve el control
de la entrada/salida a los manejadores de fichero habituales STDIN
, STDOUT
y STDERR
.