routing por diferentes proveedores

29 de noviembre de 2009

Supongamos que tenemos un servidor (en este caso un servidor de VPN) que por cualquier razón su conexión a Internet (por el RouterA) se ha quedado pequeña. Hemos decidido comprar un nuevo router simétrico (el RouterB) para garantizar la calidad de la conexiones VPN para ciertas personas. Una grupo de personas se conectarán a la VPN por el RouterA y otros por el RouterB.



Este ejemplo es extensible a cualquier otro tipo de servicio que queramos ofrecer de forma que una parte del trafico va por un sitio y el otro por otro. La dificultad radica en que el tráfico debe volver por el mismo router por el que ha llegado.

Para realizar esto utilizaremos el comando "ip" que nos permite manipular las tablas de rutas, crear políticas de rutas y crear tuneles.

Veamos como está la tabla de rutas del servidor:

root# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
192.168.2.0     *               255.255.255.0   U     1      0        0 eth1
192.168.1.0     *               255.255.255.0   U     1      0        0 eth0
link-local      *               255.255.0.0     U     1000   0        0 eth1
default         192.168.1.1     0.0.0.0         UG    0      0        0 eth0

root# ip route show
192.168.2.0/24 dev eth1  proto kernel  scope link  src 192.168.2.2  metric 1
192.168.1.0/24 dev eth0  proto kernel  scope link  src 192.168.1.6  metric 1
169.254.0.0/16 dev eth1  scope link  metric 1000
default via 192.168.1.1 dev eth0  proto static
Tanto el comando "route" e "ip route show" muestran la misma información. El comando "route" es la versión ligera del comando "ip route show".

Cuando Linux necesita enrutar un paquete busca dentro de las tablas su camino. Por defecto hay tres tablas de enrutamiento: local, main y default.
root# ip rule list
0: from all lookup local
32766: from all lookup main
32767: from all lookup default

Veamos que hay dentro de cada una de ellas:
root# ip route list table local
broadcast 192.168.1.0 dev eth0  proto kernel  scope link  src 192.168.1.6
broadcast 192.168.2.255 dev eth1  proto kernel  scope link  src 192.168.2.2
broadcast 127.255.255.255 dev lo  proto kernel  scope link  src 127.0.0.1
local 192.168.1.6 dev eth0  proto kernel  scope host  src 192.168.1.6
broadcast 192.168.1.255 dev eth0  proto kernel  scope link  src 192.168.1.6
broadcast 192.168.2.0 dev eth1  proto kernel  scope link  src 192.168.2.2
local 192.168.2.2 dev eth1  proto kernel  scope host  src 192.168.2.2
broadcast 127.0.0.0 dev lo  proto kernel  scope link  src 127.0.0.1
local 127.0.0.1 dev lo  proto kernel  scope host  src 127.0.0.1
local 127.0.0.0/8 dev lo  proto kernel  scope host  src 127.0.0.1

root# ip route list table main
192.168.2.0/24 dev eth1  proto kernel  scope link  src 192.168.2.2  metric 1
192.168.1.0/24 dev eth0  proto kernel  scope link  src 192.168.1.6  metric 1
169.254.0.0/16 dev eth1  scope link  metric 1000
default via 192.168.1.1 dev eth0  proto static

root# ip route list table default
(vacia)

Ver como la tabla por defecto del sistema (la que vemos al hacer "route") se llama main.

Lo que vamos hacer es crear dos tablas más (lan1 y lan2) para que contengan las rutas para cada una de las diferentes salidas a Internet. Para ello editaremos el archivo /etc/iproute2/rt_table con el siguiente contenido:
root# cat /etc/iproute2/rt_tables
#
# reserved values
#
255     local
254     main
253     default
0       unspec
#
# local
#
#1      inr.ruhep
10 lan1
20 lan2

Ahora crearemos las rutas que irán en cada una de estas tablas. Para que estas rutas se carguen en el sistema cada vez que arranquemos, la meteremos en /etc/rc.local:
root# cat /etc/rc.local
#!/bin/sh -e

ip rule add from 192.168.1.6 table lan1
ip route add 192.168.1.0/24 dev eth0 src 192.168.1.6 table lan1
ip route add default via 192.168.1.1 table lan1

ip rule add from 192.168.2.2 table lan2
ip route add 192.168.2.0/24 dev eth0 src 192.168.2.2 table lan2
ip route add default via 192.168.2.1 table lan2

exit 0
root# reboot

Estas órdenes aseguran que el tráfico que proviene de una interfaz, es contestado por la misma interfaz.

Una vez reiniciado el sistema vemos si las rutas se han cargado correctamente en el sistema:
root# ip rule list
0:      from all lookup local
32764:  from 192.168.2.2 lookup lan2
32765:  from 192.168.1.6 lookup lan1
32766:  from all lookup main
32767:  from all lookup default

root# ip route list table lan1
192.168.1.0/24 dev eth0  scope link  src 192.168.1.6
default via 192.168.1.1 dev eth0

root# ip route list table lan2
192.168.2.0/24 dev eth0  scope link  src 192.168.2.2
default via 192.168.2.1 dev eth1

Cuando un paquete llega al servidor este comprueba por orden si se cumple alguna de las reglas de tabla. Primero mira si se cumple "from 192.168.2.2". Si se cumple utiliza la tabla "lan2", de lo contrario pasará a ver si se cumple "from 192.168.1.6". Si se cumple utilizará la tabla "lan2". Por ultimo utilizará la tabla por defecto "main".

Para depurar y ver que todo funciona se puede utilizar el comando tcpdump y ver si el trafico pasa por una interfaz o u otra.

El siguiente escenario también lo podríamos utilizar por ejemplo para un servidor de correo donde queremos que el trafico del webmail (puerto 80 y 443) vaya por una ISP y el trafico al 25 vaya por otro ISP.

Más información:
+ Linux Advanced Routing & Traffic Control HOWTO

mi script de backups

8 de noviembre de 2009

Cuando empiezas hacer copias de backups, se suele empezar por un pequeño tar, luego lo programas diariamente, te envías un report por correo, luego lo envías a otra maquina y finalmente terminas haciendo copias completas e incrementales.

El siguiente script en Bash es el que utilizo para realizar copias diarias. Se ejecuta cada día en un cron. Los sábados realiza un backup completo y el resto de dias un incremental. Realiza un backup en local y también lo envía por SSH a una máquina remota.

Descargar kbackup.sh.

#!/bin/bash

### modificar las variables segun las necesidades

DATA_BACKUP="/etc/network /etc/ssh /etc/rc.local /var/log/syslog /var/log/auth.log
 /var/log/messages /var/log/dmesg /var/log/daemon.log"
LOCAL_DST_BACKUP="/var/backuplocal"
NAME_BACKUP="mi_srv"

SEND_REMOTE_BACKUP="yes"
REMOTE_DST_BACKUP="/var/backup/mi_srv"
HOST_REMOTE_BACKUP="srvbackup.miempresa"

DAY_FULL_BACKUP="Sun"

### variables especificas del script. No modificar

_LIST="/tmp/backup$$.list"

echo "kbackup.sh v0.1b, por amperis[@]gmail.com"
echo

case $1 in
   --help)
      echo "Sintaxis:"
      echo "  # ./kbackup.sh "
      echo
      echo "  --help: muestra la ayuda"
      echo "  --exec: ejecuta el script"
      echo
      echo "Notas:"
      echo
      echo "1) Editar este script y modificar las variables necesarias."
      echo "   DATA_BACKUP: Lista de directorio o fichero que de desean incluir en 
               el backup."
      echo "   LOCAL_DST_BACKUP: Directorio local donde se guardará la copia local 
               del backup."
      echo "   NAME_BACKUP: Nombre o prefijo del backup."
      echo "   SEND_REMOTE_BACKUP: yes/no, indicar si queremos que el backup tambien
               se copie"
      echo "      en un servidor remoto."
      echo "   REMOTE_DST_BACKUP: Directorio remoto donde debe copiarse el backup."
      echo "   HOST_REMOTE_BACKUP: Nombre de host o IP de la maquina remota que 
               almacena el"
      echo "      backup."
      echo "   DAY_FULL_BACKUP: Día de la semana que queremos hacer el full backup"
      echo
      echo
      echo "2) Crear una tarea programada diaria para este script."
      echo
      echo "   # crontab -e"
      echo "   @daily /root/script/kbackup.sh | mail -s 'Report de Backup' 
               admin@miempresa.com"
      echo
      echo "3) Para realizar el backup remoto es necesario que el servidor destino
               confie"
      echo "   en cliente. Copia su llave al servidor (http://amperis.blogspot.com
               /2009/02/scp-entre-maquinas.html)"
      echo     
      exit 0
      ;;

   --exec)
      echo "-> Inciado a las `date +%H:%M:%S`h"

      if [ -d "$LOCAL_DST_BACKUP" ]; then
        set $(date)
        ## Backup completo. Se realiza solo los sabados
        if test "$1" = "$DAY_FULL_BACKUP" ; then

           echo "-> Ejecutando copia completa del $3 de $2 del $6"
           _NAME_BACKUP="$NAME_BACKUP-full-$6-$2-$3.tgz"
           tar cfzv "$LOCAL_DST_BACKUP/$_NAME_BACKUP" $DATA_BACKUP
           rm -f $LOCAL_DST_BACKUP/$NAME_BACKUP-incr*

        else

           ## Backup incremental, se realiza el resto de dias
           echo "-> Ejecutando copia incremental del $3 de $2 del $6 ..."
           find $DATA_BACKUP -depth -type f \( -ctime -1 -o -mtime -1 \) -print > $_LIST
           _NAME_BACKUP="$NAME_BACKUP-incr-$6-$2-$3.tgz"
           tar cfzTv "$LOCAL_DST_BACKUP/$_NAME_BACKUP" "$_LIST"
           rm -f "$_LIST"

         fi

         echo "-> Backup creado: $_NAME_BACKUP"
         ## Enviado el backup a un servidor externo
         if [ $SEND_REMOTE_BACKUP = "yes" ]; then
            echo "-> Enviando backup a $HOST_REMOTE_BACKUP"
            rsync -avz $LOCAL_DST_BACKUP/ -e ssh root@$HOST_REMOTE_BACKUP:$REMOTE_DST_BACKUP
         fi

      else
         echo "ERROR: El directorio $LOCAL_DST_BACKUP no existe"
      fi
    
      echo "-> Finalizado a las `date +%H:%M:%S`h"
      ;;
   *)
      echo "Utilice la opción --help para más información."
      ;;
  
esac