|
|
Este documento está disponible en los siguientes idiomas: English Castellano Deutsch Francais Italiano Nederlands Russian Turkce Polish |
por Erdal Mutlu <erdal(at)linuxfocus.org> Sobre el autor: Erdal es uno de los editores turcos de LF. Actualmente trabaja como administrador de sistemas para Linotype Library. Siendo un gran fan de Linux desde sus años de universitario, le gusta trabajar y desarrollar en este entorno. Taducido al español por: Javier Gómez Sierras <jgomsi(at)obelix.umh.es> Contenidos:
|
Automatizando la administración de sistemas con ssh y scpResumen:
si tienes un gran número de sistemas Linux/Unix que administrar, entonces
ciertamente necesitarás algunos scripts para ayudarte a automatizar
algunas de tus tareas. Deberías haberte dado cuenta durante el trabajo de
todos los días que haces las mismas o similares cosas en cada equipo.
Quizá hallas pensado en alguna forma de automatizar estas tareas. Esto es
especialmente cierto para mantener grandes números de equipos Linux/Unix
que están configurados identicamente. En este artículo mostraré una manera
de hacer esto usando las utilidades de ssh.
|
Para seguir este artículo necesitarás algunos conocimientos básicos de programación en shell. Para conocer algo más sobre la programación en shell lee el artículo de LinuxFocus de Katja and Guido Socher llamado LinuxFocus' Programación en Shell escrito por Katja y Guido Socher. También tendrás que conocer las utilidades de ssh, como ssh-keygen, ssh-add, ssh, scp o sftp. Hay una implementación libre del protocolo SSH bajo Linux: OpenSSH, que contiene todas estas herramientas. Las páginas man también están disponibles.
scp /ruta/al/fichero/fichero1 usuario@ordenador_remoto:/directorioremoto/nuevofichero
En este ejemplo se copia el fichero con nombre fichero1 de la carpeta local al ordenador remoto (ordenador_remoto puede ser la IP o nombre de la máquina remota) en en directorio /directorioremoto con el nuevo nombre "nuevofichero". Se te pedirá que te autentifiques como úsuario'. Si te autentificas correctamente y el usuario remoto tiene los persisos necesarios, se copiará el fichero. Uno puede omitir el nombre de fichero de destino. En este caso el fichero se copiará con el nombre original. En pocas palabras, esto significa que se puede renombrar los ficheros mientras se copian.scp usuario@ordenador_remoto:/directorioremoto/fichero /ruta/a/la/carpeta/local/nuevofichero
Hay también una característica muy buena del comando scp. Puedes copiar directorios recursivamente especificando la opción '-r'.scp -r usuarior@ordenador_remoto:/directorioremoto .
El comando anterior copia el directorio 'directorioremoto' y todos sus directorios y ficheros del ordenador remoto al directorio en el que estemos con el mismo nombre.Nota: Se asume que tienes el demonio sshd corriendo en el host remoto.
ssh [email protected] df -H
Esto se parece bastante a la sintaxis de remote login (rlogin). La única difecencia está después de la parte del nombre de máquina. El comando (en este ejemplo 'df -H') se escribe para que sea ejecutado en la máquina remota. La salida del comando se muestra en tu terminal.ssh-keygen -b 1024 -t dsa
Se pedirá el nombre de la clave privada. Normalmente, el nombre de la clave pública es el mismo que el de la privada añadiéndole '.pub'. En este ejemplo '-b 1024' es el número de bits en la clave a crear. Si se especifica nada se usará el valor por defecto. El parámetro '-t dsa' se usa para especificar el tipo de clave. Los valores posibles son 'rsa1' para la versión 1 del protocolo y 'rsa' o 'dsa' para la versión 2. Yo recomiendo usar la versión 2 de SSH. Pero si tienes servidores antiguos que sólo soportan la versión 1 del protocolo necesitarás especificar '-t rsa1' y crar otro par de claves. Puedes forzar a ssh a usar la versión 1 o 2 del protocolo especificando un '-1' o '-2' respectivamente.Para usar tu clave, debes instalar tu clave pública en la máquina remota. El contenido del fichero de clave pública debería ser copiado o añadido al fichero $HOME/.ssh/authorized_keys o $HOME/.ssh/authorized_keys2. Ten cuidado y no mezcles las claves para los diferentes versiones del protocolo. El fichero authorized_keys se usa para la versión 1 del protocolo. El authorized_keys2 se usa para la versión 2. Si instalaste tu clave pública correctamente, la próxima vez que te conectes a ese ordenador tendrás que introducir tu passphrase y si no es correcta, entonces tendrás que introducir la contraseña del usuario remoto. Puedes restringir la conexión a tu equipo para que sólo se use la autentificación de clave pública editando el fichero de configuración del sshd. El nombre del fichero es/etc/ssh/sshd_config y el parámetro que tienes que cambiar es 'PasswordAuthentication'. Cambia el valor de este parámetro a no (PasswordAuthentication no) y reinicia tu sshd.
Hasta este punto todo está bien. Tenemos una forma segura de copiar y ejecutar comandos en una máquina remota. Pero, para automatizar algunas tareas no deberíamos necesitar teclear contraseñas o passphrases. Porque si no, no podremos automatizar nada. Una solución podría ser escribir en cada script que lo requiera la contraseña o passhrase, que no es una para nada una buena idea. La mejor forma es usar el gestor de claves (key-agent) para que maneje nuestras passphrases. ssh-agent es un programa para gestionar las claves privadas usadas para la autentificación con clave pública. Inicia un gestor de claves :
ssh-agent $BASH
y añádele tus claves privadas usandossh-add .ssh/id_dsa
ossh-add .ssh/identity
id_dsa es fichero de la clave privada DSA e identity es el fichero de la clave privada RSA1. Estos son los nombres de fichero por defecto dados durante la generación de claves via ssh-keygen. Por supuesto se te pedirá que introduzcas tu passphrase antes de que el ssh-add añada tu clave al ssh-agent. Puedes listar las claves añadidas con en siguiente comando:ssh-add -l
Ahora, si te conectas a un servidor que tenga tu clave en el fichero autorizado, entonces ¡estarás automaticamente conectado sin tener que teclear nada! El ssh-agent se encargará del proceso de autentificación.
Cuando se usa ssh-agent como se ha descrito arriba, sólo se puede usar dentro del terminal donde se inició el ssh-agent. Si se desea usar el ssh-agent desde cualquier terminal que se abra, es necesario hacer un poco más de trabajo. Yo escribí el siguiente pequeño script para iniciar el agente:
#!/bin/sh # # Erdal mutlu # # Iniciando un ssh-agent para ser usado en tareas programas (batch). fichero_agent_info=~/.ssh/agent_info if [ -f $fichero_agent_info ]; then echo "Fichero agent info : $fichero_agent_info existe." echo "asegúrese de que ningún ssh-agent se está ejecutando y después borre este fichero." exit 1 fi ssh-agent | head -2 > $fichero_agent_info chmod 600 $fichero_agent_info exit 0
El anterior script comprueba si existe un fichero llamado agent_info en el directorio personal del usuario que es donde estan normalmente los ficheros de los usuarios de ssh. En nuestro caso el directorio es '.ssh/'. Si el fichero existe se le avisa al usuario que existe el fichero y muestra un pequeño mensaje sobre qué puede hacerse. Si el usuario no está ejecutando ya el ssh-agent entonces el o ella tiene que borrar el fichero e iniciar el script de nuevo. El scripts ejecuta el ssh-agent y captura las dos primeras líneas en el fichero agent_info. Esta información será utilizada por otras utililidades de ssh más tarde. Lo siguiente es una línea para cambiar el modo del fichero, de manera que sólo el propietario del fichero pueda leerlo y escribir en él.
Cuando tu agente esté ejecutandose puedes añadirle tus claves. Pero antes de esto, debes hacer un source del fichero agent_info, de manera que las herramientas ssh sepan dónde está tu agente:
source ~/.ssh/agent_info or . ~/.ssh/agent_info
Y añade tus claves con ssh-add. Puedes añadir las siguientes líneas a tu fichero .bashrc de manera que cada vez que habras un terminal nuevo, se realice automaticamente el source del fichero agent_info:
if [ -f .ssh/agent_info ]; then . .ssh/agent_info fi
AVISO: Debes asegurar la máquina desde la que uses ssh-agent y el script automático que voy a describir aquí. De lo contrario, si alguien tiene accesto a tu cuenta, entonces él o ella tendrá acceso a todos los servidores a los que tú tengas acceso con las claves ssh. ¡Todo tiene un precio!
Es hora de explicar cómo vamos a automatizar algunos trabajos de un administrador de sistemas. La idea es ejecutar una serie de comandos para una lista de máquinas y descargar o subir una serie de ficheros desde o a estas máquinas. Esto es lo que los administradores de sistemas tienen que hacer a menudo. Aquí está el script:
#!/bin/sh # Instalando cualquier cosa usando Secure SHELL y SSH agent # Erdal MUTLU # 11.03.2001 ################################################################## # Funciones # ################################################################## ### Copiar ficheros entre máquinas copiar_ficheros() { if [ $fichero_de_ficheros != "ficheros_vacio.txt" ];then cat $fichero_de_ficheros | grep -v "#" | while read -r linea do direccion=`echo ${linea} | cut -d " " -f 1` fichero1=`echo ${linea} | cut -d " " -f 2` fichero2=`echo ${linea} | cut -d " " -f 3` case ${direccion} in "l2r") : ### De la máquina local (localhost) a la máquina remota (host) echo "$fichero1 --> ${host}:${fichero2}" scp $fichero1 root@${host}:${fichero2} ;; "r2l") : ### De la máquina remota a la máquina local echo "${host}:${fichero2} --> localhost:${fichero2}" scp root@${host}:${fichero1} ${fichero2} ;; *) echo "Dirección desconocida de la copia : ${direccion}" echo "Debe ser o local o remota." ;; esac done fi } ### Ejecutar comandos en máquinas remotas ejecutar_comandos() { if [ $fichero_de_comandos != "comandos_vacio.txt" ];then cat $fichero_de_comandos | grep -v "#" | while read -r linea do linea_del_comando="${linea}" echo "Ejecutando $linea_del_comando ..." ssh -x -a root@${host} ${linea_del_comando} & wait $! echo "Ejecución de $linea_del_comando OK." done fi } ### Función que engloba a las funciones ejecutar_comandos y copiar_ficheros hacerlo() { cat $fichero_de_host | grep -v "#" | while read -r host do echo "host=$host procesando..." case "${modo}" in "1") copiar_ficheros ejecutar_comandos ;; "2") ejecutar_comandos copiar_ficheros ;; *) echo "$0 : Modo desconocido : ${modo}" ;; esac echo "host=$host ok." echo "------------------------------------------------------------------" done } ################################################################## ### El programa empieza aquí ################################################################## if [ $# -ne 4 ]; then echo "Uso : $0 modo fichero_de_host fichero_de_ficheros fichero_de_comandos" echo "" echo "modo es 1 o 2 " echo " 1 : primero copia ficheros y después ejecuta comandos." echo " 2 : primero ejecuta comandos y después copia ficheros." echo "Si el nombre de ficheros.txt es ficheros_vacio.txt entonces no se procesa." echo "Si el nombre de comandos.txt es comandos_vacio.txt entonces no se procesa." exit fi modo=$1 fichero_de_host=$2 fichero_de_ficheros=$3 fichero_de_comandos=$4 fichero_agent_info=~/.ssh/agent_info if [ -f $fichero_agent_info ]; then . $fichero_agent_info fi if [ ! -f $fichero_de_host ]; then echo "Fichero de hosts : ¡$fichero_de_host no existe!" exit 1 fi if [ $fichero_de_ficheros != "ficheros_vacio.txt" -a ! -f $fichero_de_ficheros ]; then echo "Fichero de ficheros : ¡$fichero_de_ficheros no existe!" exit 1 fi if [ $fichero_de_comandos != "comandos_vacio.txt" -a ! -f $fichero_de_comandos ]; then echo "Fichero de comandos : ¡$fichero_de_comandos no existe!" exit 1 fi #### Ahora hacerlo todo hacerlo
Guardemos el script como ainstal.sh (automated installation -instalación automatizada) e intentemos ejecutarlo sin ningún parámetro. Obtendremos el siguiente mensaje:
./ainstall.sh
Uso : ./ainstall.sh modo fichero_de_host fichero_de_ficheros fichero_de_comandos modo es 1 o 2 1 : primero copia ficheros y después ejecuta comandos. 2 : primero ejecuta comandos y después copia ficheros. Si el nombre de ficheros.txt es ficheros_vacio.txt entonces no se procesa. Si el nombre de comandos.txt es comandos_vacio.txt entonces no se procesa. |
Como dice el mensaje si no quieres ejecutar ningún comando, entonces llama al argumento comandos.txt comandos_vacio.txt y si no quieres transferir ningún fichero llama al argumento fichero_de_ficheros ficheros_vacio.txt. Unas veces sólo necesitarás ejecutar unos comandos, mientras que otras sólo tranferir unos ficheros.
Antes de explicar el script línea a línea permitidme que de un posible
ejemplo de uso: Supongamos que has añadido un servidor DNS secundario a tu
red y quieres añadirlo al fichero /etc/resolv.conf. Para simplificar
supongamos también que todas tus máquinas usan el mismo fichero
resolv.conf. Por lo tanto lo único que tienes que hacer es copiar el nuevo
fichero resolv.conf a todas las máquinas.
Primero necesitas una lista de tus máquinas. Vamos a escribir todos las
máquinas en un fichero llamado hosts.txt. El formato del fichero hosts.txt
es que tal que cada línea contiene únicamente el nombre de máquina o su
IP. Aquí tienes un ejemplo:
############################################################################# #### Cada línea contiene el nombre de máquina o dirección IP de cada máquina. #### Las líneas que empiezan o contienen el carácter # se ignoran. ############################################################################# helvetica.fonts.de optima.fonts.de zaphino vectora #10.10.10.162 10.10.10.106 193.103.125.43 10.53.103.120 |
Como se puede ver en el ejemplo, se pueden especificar nombres de máquinas completos o simplemente la parte de la máquina (sin el dominio). Después es necesario crear un fichero donde se especificarán los ficheros a transferir. Hay dos tipos de transferencia posible:
Los ficheros a transferir se especifican en otro fichero. Guardemos este fichero como fichero_de_ficheros.txt. El formato del fichero fichero_de_ficheros.txt es el siguiente: Cada línea incluye información para copiar un único fichero. Hay dos posibles sentidos de copia: l2r (local a remoto) y r2l (remoto a local). l2r se usa cuando se copia un fichero de la máquina local a una máquina remota. r2l se usa cuando se copia un fichero desde una máquina remota a la máquina local. Después de la palabra clave que indica el sentido se especifican dos nombres de fichero. Dichos campos se separan por espacio o tabulador. El primer fichero se copia al segundo fichero de acuerdo con el sentido especificado por la palabra clave. El nombre de fichero para los ficheros de la máquina remota debería ser una ruta completa, de otro modo se copiará en el directorio home del root. Este es nuestro fichero_de_ficheros.txt :
############################################################################ # La estructura del fichero es : # - El significado de los campos es : es l2r (localhost a remoto) y r2l # (máquina remota a local). # r2l fichero1 fichero2 # significa copiar el fichero1 desde la máquina remota # (máquinas especificadas en el fichero hosts.txt) a la local como # fichero2. # l2r fichero1 fichero2 # significa copiar el fichero1 de la máquina local a la # remota (máquinas especificadas en el fichero hosts.txt) como fichero2 # fichero1 y fichero2 son los ficheros en las máquinas # correspondientes. # # Nota: el orden en que se usan local y remoto especifica la # dirección del proceso de copia. ############################################################################ l2r resolv.conf /etc/resolv.conf |
Como puedes ver ya he incluido una descripción de cómo se estructura el fichero. Normalmente incluyo esta descripción en cada fichero fichero_de_ficheros.txt que utilizo. Es una solución sencilla pero my buena para la documentación. En nuestro ejemplo queremos copiar el fichero resolv.conf a una máquina remota como /etc/resolv.conf. Con ánimo de demostración después de copiar el fichero a las máquinas destino añadí unos comandos para cambiar el propietario y el grupo y mostrar el contenido de /etc/resolv.conf. Los comandos a ejecutar se especifican en un fichero a parte. Al fichero con los comandos lo llamaremos fichero_de_comandos.txt. Aquí se muestra su contenido:
########################################################################### # La estructura de este fichero es: Cada línea contiene un comando a # ejecutar. Cada comando se trata por separado. ########################################################################### chown root.root /etc/resolv.conf chmod 644 /etc/resolv.conf cat /etc/resolv.conf |
El fichero de comandos contiene los comandos a ser ejecutados en cada máquina listada en el fichero hosts.txt. Los comandos se ejecutan secuencialmente, esto significa que primero se ejecuta el primer comando y después el segundo, y así con el resto.
Ok, ahora tienes todos los ficheros necesarios para este sencillo ejemplo. La única cosa que falta por especificar el la opción 'modo', que especifica cuál de los dos ficheros: fichero_de_comandos.txt o fichero_de_ficheros.txt se ha de procesar primero. Uno puede transferir los ficheros listados en fichero_de_ficheros.txt y después ejecutar todos los comandos en la máquina destino, esto es el modo=1. Y lo contrario, ejecutar los comandos y después tranferir los ficheros, que es el modo=2. Ahora puedes ejecutar el script con los argumentos necesarios como sigue:
./ainstall.sh 1 hosts.txt fichero_de_ficheros.txt fichero_de_comandos.txt
Un pequeño consejo: normalmente yo siempre añado el prefijo ficheros_ al fichero ficheros.txt y después le doy un nombre corto descriptivo, como ficheros_resolvconf.txt. También aplico la misma técnica a hosts.txt y comandos.txt.
Es hora de explicar un poco más sobre el propio script. El programa comienza comprobando el número de argumentos y si no es 4 entonces muestra el modo de empleo. Si el número de argumentos es el correcto, cada argumento es asignado a su variable correspondiente. Después si existe el fichero '~/.ssh/agent_info', se carga su contenido. Este fichero contiene información sobre el agente ssh en ejecución. Si no usas un agente, entonces tendrás que introducir las contraseñas o frases de paso manualmente, lo que significa que no hay automatización :). Después se comprueba que existe cada fichero (hosts, ficheros y comandos). También hay una comprobación especial para el fichero ficheros_vacio.txt y comandos_vacio.txt. Si especificaste dicho nombre, entonces no es necesario comprobar la existencia del fichero. He cambiado esta parte del script durante la redacción de este artículo. Antes, sólo era:
if [ -f $fichero_de_host -a -f $fichero_de_ficheros -a -f $fichero_de_comandos ]; then echo "$fichero_de_host $fichero_de_ficheros $fichero_de_comandos" hacerlo else echo "no existe $fichero_de_host o $fichero_de_ficheros o $fichero_de_comandos" exit fi
En este caso tenía que tener los ficheros con el nombre:
ficheros_vacio.txt y comandos_vacio.txt. Aunque en realidad no era un
problema, porque estaba trabajando únicamente en un directorio.
Al final vienen la llamada a la función 'hacerlo'. Todo se controla en
esta función. La función tiene un bucle hecho con 'cat' y 'while', que
para cada máquina listada en '$fichero_de_host' llama a las funciones
copiar_ficheros y ejecutar_comandos de acuerdo al 'modo'. Así que se
realiza el trabajo para cada máquina. La variable 'host' contiene el
nombre de máquina o IP actual.
Veamos la función copiar_ficheros. Esta función primero comprueba si el
valor de 'fichero_de_ficheros' es igual a 'ficheros_vacio.txt' o no. Si es
igual entonces no se hace nada. Si no, entonces en cada línea del fichero
'$fichero_de_ficheros', las variables 'direccion', 'fichero1' y 'fichero2'
contienen la dirección de la copia, el nombre del primer fichero y el
nombre del segundo respectivamente. De acuerdo con el valor de la variable
'direccion', se realiza una copia usando scp.
Para finalizar veamos lo que se hace en la función ejecutar_comandos.
La función comprueba si el valor de 'fichero_de_comandos' es igual o no a
'comandos_vacio'. Si es igual no se hace nada. Si no, entonces cada
comando del fichero '$fichero_de_comandos' se ejecuta en la máquina remota
mediante ssh. Después de ejecutar el comando ssh hay una llamada al
comando wait con el parámetro '$!'. Este comando se asegura que cada
comando se ejecuta uno detrás de otro. '$!' se expande al ID de proceso
del comando más reciente ejecutado en segundo plano.
Es todo. Simple, ¿no es cierto?
Aquí se presenta un uso más avanzado del script. La idea es hacer una copia de seguridad de los ficheros de configuración de tus máquinas o servidores. Para ello escribí este pequeño script que usa ainstall.sh :
#!/bin/sh directorio_del_servidor=${HOME}/erdal/sh/CopiasegServidor if [ ! -d $directorio_del_servidor ]; then echo "Directorio : $directorio_del_servidor no existe." exit 1 fi cd $directorio_del_servidor servidores=ll_servidores.txt prog=${HOME}/erdal/sh/einstall_sa.sh cat $servidores | grep -v "#" | while read -r host do echo $host > host.txt $prog 1 host.txt ficheros_vacio.txt servidores/${host}/comandos_hacer_copiaseg.txt $prog 1 host.txt ficheros_copiaseg.txt comandos_vacio.txt mv -f copiaseg.tgz servidores/${host}/copiaseg/`date +%Y%m%d`.tgz rm -f host.txt done exit 0
Tienes que tener un directorio especial llamado servidores. En este directorio deben haber dos ficheros: ficheros_copiaseg.txt y ll_servidores.txt. Aquí tienes el 'ficheros_copiaseg.txt' :
r2l /root/copiaseg.tgz copiaseg.tgz
'll_servidores.txt' contiene los nombres o direcciones IP de los hosts sobre los que se realizará la copia de seguridad. Cada nombre de máquina listado en el fichero 'll_servidores.txt' debe tener un directorio con el mismo nombre y dentro de este directorio debe haber un fichero llamado comandos_hacer_copiasegs.txt, que contenga un comando para hacer un fichero /root/copiaseg.tgz de los ficheros de configuración de esa máquina. Y un directorio llamado copiaseg. Todas las copias de seguridad de esa máquina se almacenarán en ese directorio. Si el contenido del fichero ll_servidores.txt es :
servidor_de_ficheros servidor_de_bbdd 10.10.10.1 servidor_de_aplicaciones |
entonces la estructura de directorio de tu directorio '$servidores' debe ser la siguiente:
servidores |-- ficheros_copiaseg.txt |-- ll_servidores.txt |-- hacer_copiaseg_servidor.sh |-- 10.10.10.1 | |-- copiaseg | `-- comandos_hacer_copiaseg.txt |-- servidor_de_aplicaciones | |-- copiaseg | `-- comandos_hacer_copiaseg.txt |-- servidor_de_bbdd | |-- copiaseg | `-- comandos_hacer_copiaseg.txt |-- servidor_de_ficheros |-- copiaseg `-- comandos_hacer_copiaseg.txt |
Y aquí tienes algunos ejemplos para los ficheros comandos_hacer_copiaseg.txt:
tar cfz /root/copiaseg.tgz /etc/samba /etc/atalk /etc/named.conf /var/named/zones
El anterior comandos_hacer_copiaseg.txt se usa para hacer una copia de seguridad de samba, atalk y la configuración del servidor de nombres y los ficheros de zonas.
tar cfz /root/copiaseg.tgz /etc/httpd /usr/local/apache
El anterior comandos_hacer_copiaseg.txt se usa para hacer una copia de seguridad de la configuración y los ficheros de un servidor apache.
tar cfz /root/copiaseg.tgz /etc/squid /etc/named.conf
El anterior comandos_hacer_copiaseg.txt se usa para hacer una copia de seguridad de las configuraciones del servidor proxy squid y el servidor de nombres secundario.
Usando el anterior script y creando los ficheros comandos_hacer_copiaseg.txt de acuerdo con tus necesidades puedes hacer copias de seguridad de las configuraciones de tus servidores.
|
Contactar con el equipo de LinuFocus
© Erdal Mutlu, FDL LinuxFocus.org |
Información sobre la traducción:
|
2003-02-28, generated by lfparser version 2.34