domingo, 28 de agosto de 2016

Crear una conexión sin passwords entre servidores utilizando ssh/sftp

Saludos y bueno y que es sftp (secure file transfer protocol)
es un protocolo para envio de archivos basado en ssh (secure shell), y bueno y para que demonios sirve?, es en realidad para transferir archivos entre servidores o bien computadoras usando un método seguro donde la información va encriptada por ssh.

Hasta aquí todo bien y así debe ser, que pasa cuando tenemos uno o más servidores y queremos transferir un archivo usando ssh ?

Bueno aquí va la receta de la casa para utilizar ssh (lo que yo uso para realizar conexiones entre servidores)

  1. server origen y/o usuario origen (si, puede ser un windows aunque no entraré mucho en el tema y además está samba ne vez de usar ssh para transferir)
  2. server destino y/o path destino
  3. el usuario origen y el usuario destino
  4. se puede usar un gestor de transferencias como filezilla o winscp o bien en este caso usaremos la poderosa terminal :-)
Ahora bien sftp tiene varios procesos que puede hacer, 2 de los más sencillos de entender y usuales es PUT y GET.
  • Put se encarga de "empujar" o subir una archivo o path que le indiquemos 
  • Get se encarga de "traer"o bajar del servidor un determinado archivo o bien path.
Requisitos de linux a linux o linux a unix like
Requisitos como ya mencione algunos:
1.- tener el usuario origen (el que va a conectarse hacia el otro servidor)
2.- tener el usuario destino
3.- contar con el password del usuario destino * este es opcional  nos facilita mucho las cosas

Ejecución:
1.- con el usuario origen el que queremos que envíe el archivo) ejecutamos el siguiente comando ( esto va a generar la llave de tipo rsa, recuerda que hay varios tipos de algoritmos; rsa suele ser el más común no por eso menos seguro)

ssh-keygen o ssh-keygen -t rsa

El comando anterior creará las llaves de autenticación en su home directory del usuario en:


~/.ssh/  (donde el tilde significa /home/miusuario)

NOTA:en este paso puede que te solicite crear un passphrase
para que sea una conexión totalmente sin password solo tecleamos enter sin poner nada en este campo, a menos que quieras especificar uno en cuyo caso recomiendo guardes bien esta contraseña ya que te será solicitada cada vez que te quieras conectar.

2.-el siguiente paso es copiar la llave publica al usuario destino en el servidor destino para eso tenemos un par de formas para realizarlo podemos utilizar el comando ssh-copy-id o enviamos/copiamos la llave pública del usuario origen al usuario receptor o destino en el server destino con: 

ssh-copy-id -i ~/.ssh/id_rsa.pub server_destino


o bien hay que entrar al servidor destino/remoto siendo el usuario origen (el que compartió la llave)

ssh usuariodestino@serverdestino o sftp usuariodestino@serverdestino

4.- Colocar la llave pública en el otro server/usuario en el archivo authorized_keys con el siguiente formato:
ssh-rsa llave user@(ip|hostname)
yo te recomiendo realizarlo con: 
cat llave_origen.pub >> authorized_keys
el archivo authorized_keys se ubicara en ~/.ssh/


Los errores más comunes :
-Revisar que el usuario destino y origen no estén bloqueados
-Si este procedimiento de arriba no funciona, lo mejor es buscar el sshd_config del servidor
de SSH y añadir las siguientes líneas, o descomentar las:

RSAAuthentication yes       (usado para checar el knownhost)
PubkeyAuthentication yes

- Revisar que el usuario origen y destino no estén bloqueados con passwd -s (solaris) passwd -S (linux)

-Revisar el tamaño de las llaves que correspondan con las proporcionadas ya que al copiar y pegar puede ser 
que se haya omitido algún caracter, vigilar el mismo numero de lineas la llave solo mide el equivalente a una sola línea de ahí que recomiendo que se utilice el "cat" para concatenar correctamente la llave al final del archivo authorized_keys en una sola línea.

Revisar
------------------------Permisos en los directorios y archivos //muy importante el home
example
user: juanito
HOME:
drwxr------  4 juanito staff 512 jun 5 09:58 /home/501924789

drwx------ 2 juanito staff 512 jun 5 09:58 /home/juanito/.ssh

-rw-r--r-- 2 juanito staff 512 jun 5 09:58 /home/juanito/.ssh/authorized_keys


En algunas ocasiones en linux pueden interferir los contextos de SElinux para esos casos hay que revisar dichos permisos

para revisar los contextos se utiliza:
ls -laZ
el siguiente ejemplo es ERRONEO por tener en el contexto de SELinux como home_ROOT_T cuando deberia tener ssh_home_t
rw-------. juanito staff unconfined_u:object_r:home_root_t:s0 authorized_keys

para cambiar o restaurar los permisos se ejecuta el siguiente comando
restorecon -r -vv ~juanito/.ssh/ donde juanito... es el home del usuario

los permisos deberían de mostrar una forma como la enlistada más abajo  para todos los archivos

[root@server.chipocludo.ssh]# ls -laZ ~juanito/.ssh/
drwx------. juanito staff unconfined_u:object_r:ssh_home_t:s0 .
drwx------. juanito staff system_u:object_r:home_root_t:s0 ..
-rw-------. juanito staff unconfined_u:object_r:ssh_home_t:s0 authorized_keys
-rw-r--r--. juanito staff unconfined_u:object_r:ssh_home_t:s0 known_hosts


Configurar 2 o más llaves simultáneas con un mismo usuario 

Aquí podremos configurar 2 llaves distintas para el mismo usuario es decir nuestro usuario origen va a poder utilizar llaves distintas para autenticarse según el destino que sea necesario o el usuario destino al crear un archivo llamado config en la carpeta ~/.ssh/
también nos da la posibilidad de utilizar llaves con diferentes cifrados es decir tener una con 1024 y otra cifrada con 2048 u otra más pero con cifrado DSA.

1.-configura que una llave este funcional es decir los pasos de arriba para RSA
2.- ya con la llave funcionando puedes moverla a una carpeta, hay que vigilar muy bien los permisos
3.- se crea la segunda llave con los mismos pasos y a esta le vamos a poner otro nombre de archivo ejemplo: id_rsa2_t
   -- nota es posible que sea necesario ejecutar :

      ssh-add id_rsa e ingresar el passphrase que utilizaste si es que tenias alguno.
4.- de igual forma se mueve a otra carpeta la segunda llave para mantener ordenado/separadas las llaves generadas

5.- creamos  el archivo config, de igual manera revisar los permisos ver el ejemplo más abajo:
touch config
6.- y agregamos la siguientes líneas dentro de config con nuestro editor de texto preferido
-bash-4.1$ vim config
Host server.chipocludo // esta linea indica el nombre del host
        User  test_ssh // el usuario al que nos vamos a conectar
        Hostname server.chipocludo // el hostname destino
        PreferredAuthentications publickey // el tipo de autenticación
        IdentityFile ~juanito/.ssh/primerallave/id_rsa //la linea mas importante aquí se indica la ruta del archivo con el que nos vamos a identificar. 

Host server.chipocludo
        User  test_ssh2
        Hostname server.chipocludo
        PreferredAuthentications publickey
        IdentityFile ~juanito/.ssh/segundallave/id_rsa2_t //la linea mas importante aquí se indica la ruta del archivo con el que nos vamos a identificar pero para el servidor 2 o el usuario dos

vamos a ver como quedo la primera llave  en la carpeta 1
-bash-4.1$ cd primerallave
-bash-4.1$ ls -ltr   -------------> en esta parte ojo con los permisos y el selinux en linux
total 8
-rw-------. 1 juanito staff  407 Feb 26 14:19 id_rsa.pub
-rw-------. 1 juanito staff  679 Feb 26 14:19 id_rsa
-bash-4.1$ cd ..
-bash-4.1$ pwd // mas o menos asi queda tu carpeta
/home/juanito/.ssh
-bash-4.1$ ls -ltr
total 20
drwxr-xr-x. 2 juanito staff 4096 Feb 26 14:27 segundallave
drwxr-xr-x. 2 juanito staff 4096 Feb 26 14:27 primerallave
-rwxr-xr-x. 1 juanito staff  432 Feb 26 14:46 known_hosts
-rw-r--r--. 1 juanito staff  406 Feb 26 15:12 authorized_keys
-rwxr-xr-x. 1 juanito staff  392 Feb 26 15:28 config ----> este es el archivo


otro ejemplo utilizando el config file
Host github.com
User git
Hostname github.com
PreferredAuthentications publickey
IdentityFile ~/.ssh/git/id_rsa
Host fedoraproject.org
Hostname fedoraproject.org
PreferredAuthentications publickey
IdentityFile ~/.ssh/fedoraproject/id_rsa
Host fedorapeople.org
Hostname fedorapeople.org
PreferredAuthentications publickey
IdentityFile ~/.ssh/fedoraproject/id_rsa

ejemplo: 3 
Host *.dominio.chipocludo.com
        PreferredAuthentications publickey
        IdentityFile ~juanito/.ssh/basekey/id_rsa
Host sufruta.mm.com
        User aldo
        Hostname sufruta.mm.com

        PreferredAuthentications publickey

referencia: http://www.robotgoblin.co.uk/blog/2012/07/24/managing-multiple-ssh-keys/








Activar el modo debug en bash

Yo se me van a amar aquellos que apenas empiezan como yo en el mundillo de bash y la programación en shell script

veamos cuando la opción x en bash es agregada, bash imprimirá cada comando que ejecutará antes de ejecutarlo en la salida de standard error. eso después de aplicar cualquier expansión . como resultado  tu podras ver lo que ocurre. hay que poner especial atención en el entrecomillado. Bash utiliza las comillas para presentar exactamente las cadenas que son pasadas al script como argumentos únicos 

Básicamente hay tres formas de activar el modo debug
  • Correr el script con bash -x:
    $ bash -x ./scriptsito_del_mal
  • Cambiar el shebang (header):
    #!/bin/bash -x
    [.. script ..]
  • O:
    #!/usr/bin/env bash
    set -x
  • O agregar set -x agregar -.x o +x en bloques específicos de tu codigo para prender o apagar el modo debug y solo afectar a esa parte de tu código:
    #!/usr/bin/env bash
    [..aquí no importa este código..]
    set -x
    [..aquí había problemas activar el modo debug..]
    set +x
    [..aqui ya no esta activo el modo debug ..]
la salida del modo debug usualmente se despliega en stderr,lo que implica que la observaras en la pantalla de la terminal. pero como siempre podemos utilizar una tubería para redireccionar la salida a un archivo y evitar que se despliegue todo en pantalla:
exec 2>> /home/miarchivito.out
set -x


Ideas para automatizar un actualización de jdk java con bash

Bueno les dejo un script que hace tiempo que desarrolle quizás necesite depurarse y customizarse para que puedan ustedes emplearlo, la idea de publicarlo es que sirva como estudio y pudiera ser empleado como ejemplo para el desarrollo de otros script es bastante simple y no considero mi fuerte el desarrollo, la idea básicamente es utilizar un servidor apache como repositorio, descargar los paquetes de ahí y después realizar la instalación, evidentemente parte de este código fue empleado para un cliente el cual omitiré nombrar por cuestiones y modificare el código para conservar su privacidad,
iré explicando los pasos linea por linea para que se entienda lo mejor posible con las acostumbradas  "//"
DESARROLLADO EN RED HAT 6 Y 7

#!/bin/bash                             // declaramos el shebang en este caso bash
#Author: JCPG licence GPL // aqui me cuelgo mis medallas XD jajaja
#Script to install a new java version ##### untested version SCRIPT Java_install_RC5_FINAL  ###
#Script modificado para instalar la version java 1.6.0_115 to 1.6.0_121  and 1.7.0_101 to 1.7.0_111
# Test servers Server 1 (java 7) and server tipo 2 (java 6) // el asunto aqui es que tenia un grupo de servidores con java 7 y otros con java 6 así que para evitar errores y no crear scripts por separado le di facultades (un case :-P ) para que el script pudiera distinguir la instalación que requería el server, cabe mencionar que ya se conoce la versión previa de java en cada caso

#Variables to retrieve the version and take java.security
VERSION=`rpm -q jdk | sort -r | head -1`  //asigno a esta variable la versión de java previamente instalada utilizando el manejador de paquetes

PACKAGE=p23218364_160121_Linux-x86-64.zip // el nombre de mi paquete java 6
PACKAGE2=p23218354_170111_Linux-x86-64.zip  // el nombre de mi paquete java 7
JAVAPKGOLD1=jdk-1.6.0_115-fcs.x86_64 //el nombre del paquete java 6 instalado
JAVAPKGOLD2=jdk-1.7.0_101-fcs.x86_64 //el nombre del paquete java 7 instalado
JAVAPKGBKP1=jdk1.6.0_115  // nombre de la carpeta del java 6 anterior
JAVAPKGBKP2=jdk1.7.0_101 // nombre de la carpeta del java 7 anterior
JAVAPKGNEW1=jdk1.6.0_121 // nombre de la carpeta del java 6 nueva
JAVAPKGNEW2=jdk1.7.0_111  // nombre de la carpeta del java 7 nueva
NEWRPM1=jdk-6u121-linux-x64-rpm.bin //el nombre del paquete java 6 descomprimido
NEWRPM2=jdk-7u111-linux-x64.rpm //el nombre del paquete java 6 descomprimido
OLDJAVAFOLDERS=jdk1.[6-7].?_??_old // carpeta backup existente o por existir
BACKUP_DATE=`/bin/date +%Y%d%H%M%S`  // fecha
VERSIONNEW=`rpm -qi jdk | head -2 | grep -i version | awk '{print $2}'` // extraigo la nueva version


#la función pre-patch valida que este instalado unzip, wget, y yum que son programas necesarios para # la operación del script de igual manera se valida si ya existe el folder de java

function pre_patch {
if [ -f "/usr/bin/unzip" ]
 then
echo "unzip installed"
else
 echo "unzip not installed trying to install it"
 yum install -y unzip
fi
if [ -f "/usr/bin/wget" ]
 then
echo "wget installed"
else
 echo "wget not installed trying to install it"
 yum install -y wget
fi
if [ -f "/usr/bin/yum" ]
  then
   echo "yum installed"
 else
   echo "Error yum not installed and it is a necessary package"; exit 2;
fi
if [ -d "/usr/java" ]
 then
  echo "Java path found"
 else
  echo "Error /usr/java path doesn't exists"; exit 2;
 fi

}

## esta es la función principal que básicamente es la que instala el java ##

function install_java {
LINK=`ls -ld [i,w,d]??_java | awk '{print $9}'`  // esta variable extra el nombre de un softlink que puede cambia al que el aplicativo requería ingresar esta con una expresión regular ya que dependiendo del server cambia su nombre ( no me pregunte por que rayos estaba así pero ellso llamaban al java asi )

## comienza el case ## // aqui validamos que versión manejamos
case "$VERSION" in

  "$JAVAPKGOLD1" ) // para java 6
     echo "java found:"$VERSION // confirmo que encontre la version
     wget -nv http://server01.chipocludo/java/$PACKAGE // descargo el paquete de java
    mv $JAVAPKGBKP1 "$JAVAPKGBKP1$BACKUP_DATE" || { echo "Could not move folder $JAVAPKGBKP1"; exit 2; } // hago un pequeño backup moviendo la carpeta actual ( no necesita ser exacto en este caso)
    unzip $PACKAGE || { echo "Could not unzip package $PACKAGE"; exit 2; } // descomprimo el paquete
    mv readme.txt readme.txt_"$BACKUP_DATE" || { echo "Could not move old readme.txt under /usr/java/"; exit 2; } // hago un backup de el readme.txt que explica los cambios del release anterior
    yum remove -y jdk // remuevo cualquier version de jdk previamente instalada
    yes | ./$NEWRPM1 // como el paquete es un rpm.bin (como un exe para no entrar en detalles) utilizo yes mas una tubería para aceptar por default los valores necesarios y forzar de alguna manera a que sea "desatendido"

    cp /usr/java/"$JAVAPKGBKP1$BACKUP_DATE"/jre/lib/security/*.jar /usr/java/$JAVAPKGNEW1/jre/lib/security/ || { echo "Could not move JARS under /usr/java/$JAVAPKGNEW2/jre/lib/security"; } //creo un backup de los jars security que dificilmente se actualizan entre versiones en este casos son estos los que utiliza la app

    cp -p /usr/java/$JAVAPKGNEW1/jre/lib/security/java.security /usr/java/$JAVAPKGNEW1/jre/lib/security/java.security_"$BACKUP_DATE" || { echo "WARNING: Could not backup java.security"; } //copio el archivo security al nuevo folder de la nueva version

    sed 's/jdk.tls.disabledAlgorithms=SSLv3, MD5withRSA, DH keySize < 768/\#jdk.tls.disabledAlgorithms=SSLv3, MD5withRSA, DH keySize < 768/g' /usr/java/$JAVAPKGNEW1/jre/lib/security/java.security_"$BACKUP_DATE" > /usr/java/$JAVAPKGNEW1/jre/lib/security/java.security // esta linea se vuelve algo muy extraño de la app que no es compatible con certificados SSL y cifrados so comento una linea para omitir esta operacion dentro de java.security

    unlink $LINK || { echo "WARNING: Could not delete the softlink $LINK"; } // quito el softlink anterior ya que necesita se actualizado y reapuntarse a la nueva carpeta donde esta instalado jdk
    ln -s /usr/java/$JAVAPKGNEW1 $LINK || { echo "WARNING: Could not create the softlink $LINK"; } //y creamos el nuevo link
    grep SSLv3 /usr/java/$JAVAPKGNEW1/jre/lib/security/java.security // hago este grep solo para validar que la linea fue modificada correctamente y se muestre en pantalla el comentario
remove_old // mando llamar a la función remove_old para depurar los backups que pudieran existir y liberar espacio
;;
// no comentare todo el case para la versión 7 pero es bastante parecido.
"$JAVAPKGOLD2" ) // para java 7
    echo "java found:"$VERSION
    wget -nv http://server01.chipocludo/java/$PACKAGE2
    mv $JAVAPKGBKP2 "$JAVAPKGBKP2$BACKUP_DATE" || { echo "Could not move folder $JAVAPKGBKP2"; exit 2; }
    unzip $PACKAGE2 || { echo "Could not unzip package $PACKAGE"; exit 2; }
mv readme.txt readme.txt_"$BACKUP_DATE" || { echo "Could not backup old readme.txt under /usr/java/"; exit 2; }
    yum remove -y jdk
    yum localinstall -y $NEWRPM2 // a diferencia de la versión 6 el jdk 7 es un rpm normalito y puede ser instalado o bien con yum, o dnf o rpm, en este caso como yo proveo el rpm utilizo localinstall y el -y para asumir yes a cualquier pregunta del instalador
    cp /usr/java/"$JAVAPKGBKP2$BACKUP_DATE"/jre/lib/security/*.jar /usr/java/$JAVAPKGNEW2/jre/lib/security/ || { echo "Could not move JARS under /usr/java/$JAVAPKGNEW2/jre/lib/security"; }
    cp -p /usr/java/$JAVAPKGNEW2/jre/lib/security/java.security /usr/java/$JAVAPKGNEW2/jre/lib/security/java.security_"$BACKUP_DATE" || { echo "WARNING: Could not backup java.security"; }
    sed 's/jdk.tls.disabledAlgorithms=SSLv3, MD5withRSA, DH keySize < 768/\#jdk.tls.disabledAlgorithms=SSLv3, MD5withRSA, DH keySize < 768/g' /usr/java/$JAVAPKGNEW2/jre/lib/security/java.security_"$BACKUP_DATE" > /usr/java/$JAVAPKGNEW2/jre/lib/security/java.security // la linea del sed cambia un poco en el reemplazo del archivo pero básicamente se comenta igual
    unlink $LINK || { echo "WARNING: Could not delete the softlink $LINK"; }
    ln -s /usr/java/$JAVAPKGNEW2 $LINK || { echo "WARNING: Could not create the softlink $LINK"; }
    grep SSLv3 /usr/java/$JAVAPKGNEW2/jre/lib/security/java.security
   remove_old
;;
 "*" ) // para cualquier otra condición o versión de java
    echo "Wrong Java Version or not previously installed"
;;
esac
 
}

#esta es una función para validar que versión se encuentra instalada y que sea una de las actuales#
function validation {

if [ "$VERSIONNEW" = "$JAVAPKGNEW1" ] ||  [ "$VERSIONNEW" = "$JAVAPKGNEW2" ]
 then
   echo "the installed Java is up to date: $VERSIONNEW"
   exit 0
 else
      install_java
 fi

}

#esta función se lleva o elimina los folders y paquetes utilizados en la instalación y paquetes viejos que se quedaron cuando el proceso se realizaba a mano la salida de lo que se borra se guarda en /var/tmp/error_removejava.out igual lo pueden guardar en /tmp o donde mas les plasca#
function remove_old {
## function that delete old files and rpm,bin,tar and gz files used during the installation ##

echo "Log Old files removed under /usr/java $BACKUP_DATE" > /var/tmp/error_removejava.out
rm -R $OLDJAVAFOLDERS >> /var/tmp/error_removejava.out 2>&1 || { echo "WARNING: Could not delete old folder $OLDJAVAFOLDERS"; }
rm -fv /usr/java/*[Ll]inux*.[rbz][pi][mnp] >> /var/tmp/error_removejava.out 2>&1 || { echo "WARNING: Could not delete old *.gz *.bin .rpm files"; } // adios a los rpm,bin y zips igual se puede hacer con [rpm,bin,zip] y es algo mas preciso que la expresion regular que igual se me hizo un buen ejercicio jeje
rm -fv /usr/java/sun-javadb-*.rpm >> /var/tmp/error_removejava.out 2>&1
echo "---- End task Log Old files removed under /usr/java $BACKUP_DATE ---" >> /var/tmp/error_removejava.out  // aqui elimino un tipo muy especifico de archivo que son los sun-javadb

}
####### Main program ##### // y esta es la secuencia del programa
pre_patch  // entra la funcion pre patch para validar los requisitos
validation // validamos que no este instalado y procedemos a instalar o si ya esta instalado es exitoso
cd /usr/java // nos posicionamos en el folder donde se va a instalar
validation // validamos que no este instalado y procedemos a instalar o si ya esta instalado es exitoso

espero los comentarios sean claros y sirva como un caso de estudio igual para no llamar 2 veces a la función de validation podemos agregar un ciclo y etc... pero me pareció bastante simple así y no considere necesario iterar mas veces.
espero les sirva y aporte algunas ideas para sus scripts.