Mostrando entradas con la etiqueta apt-get. Mostrar todas las entradas
Mostrando entradas con la etiqueta apt-get. Mostrar todas las entradas

sábado, 25 de febrero de 2012

Buscando una IP libre en mi red

Buenas, tanto tiempo!!! Definitivamente me cuesta alcanzar una frecuencia aceptable de entradas al blog, ya no cabe duda.

En fin, hoy les traigo un pequeño aporte que para algunas podría ser muy útil y para otros unas líneas de código casi inútiles xD.

El problema que intenta resolver es el siguiente: cuando uno administra una cantidad considerable de direcciones IP, digamos como mínimo una clase C fragmentada por ejemplo en 4 redes, resulta complicado saber qué direcciones están disponibles para asignar y cuáles están en uso.

Para existen las siguientes soluciones:
  • Tener una memoria perfecta y recordar precisamente todas las IPs libres y las asignadas.
  • Llevar registro con alguna herramienta.
  • Recorrer el rango de direcciones en busca de ellas.
Debo admitir que carezco de memoria perfecta, y no conozco una herramienta sencilla que resuelva el problema, por lo tanto opté por la tercera solución.


#!/usr/bin/perl -w
use Sys::Hostname;
use Socket;

####functions
#turns a number to an IP (dot-decimal notation)
sub pickAddress
{
    my $A = $_[0];
    my $aux;
    my $S="";
   
    $aux=$A & 4278190080;#first byte
    $aux=$aux>>24;
    $S=int($aux).".";#127.
   
    $aux=$A & 16711680;#second byte
    $aux=$aux>>16;
    $S=$S.int($aux).".";#127.0.

    $aux=$A & 65280;#third byte
    $aux=$aux>>8;
    $S=$S.int($aux).".";#127.0.0.
   
    $aux=$A & 255;
    $S=$S.int($aux);#127.0.0.1

    return $S;
}

#returns up if the host answer echo requests
sub state
{
    my $IP = $_[0];
    my $line;
    my $rtt;
    @SALIDA=`ping -nfq -w 1 -c 2 $IP`;
    while ($line=pop(@SALIDA))
    {
        if($line=~/0 received/)
        {
            return extraTest($IP);
        }
    }
    $rtt="UP";
    return $rtt;
}

#a running host might not answer echo requests, so lets try some common open ports.
sub extraTest
{
    my $IP=$_[0];
    @SALIDA=`nmap $IP -p T:25,22,21,53,80,110,443,U:53 2>/dev/null`;
    while ($line=pop(@SALIDA))
    {
        if($line=~/0 hosts up/)
        {
            $rtt="DOWN";
            return $rtt;#so it seems to be down... any other test?
        }
    }
    $rtt="UP";
    return $rtt;
}
####end of functions

####main code
if($< != 0)
{
    print STDERR "You must be root!\n";
    exit(-3);
}

$NET_ADDR="";
$NET_MASK="";
$HOSTS="";
$FIRST="";
$LAST="";

$argc=@ARGV;
if($argc < 1 or $argc > 2)
{
    print STDERR "USE: script [mask_bits]\n";
    exit(-1);
}

$NET_ADDR=$ARGV[0];
$NET_MASK=$ARGV[1];

#is it a real IP address?
if($NET_ADDR!~/^([\d]+)\.([\d]+)\.([\d]+)\.([\d]+)$/)
{
    print STDERR "Invalid network address. Example: 192.168.0.1\n";
    exit(-4);
}

#valid mask?
if($argc == 2 and ($NET_MASK > 30 || $NET_MASK < 1))
{
    print STDERR "Invalid mask. Mask [1 .. 30]\n";
    exit(-2);
}


#from 127.0.0.1 to 01111111 0000000 00000000 00000001
@L=split('\.',$NET_ADDR);
$NET_ADDR=$L[0]<<24;
$NET_ADDR=$NET_ADDR | $L[1]<<16;
$NET_ADDR=$NET_ADDR | $L[2]<<8;
$NET_ADDR=$NET_ADDR | $L[3];

if($argc == 2)
{#subnet check
    $HOSTS=2**(32-$NET_MASK) - 2;
   
    $aux=$NET_MASK;
    $NET_MASK=1;
    for($i=0;$i<32;$i++)
    {
        if($aux>0)
        {
            $NET_MASK=$NET_MASK<<1;
            $NET_MASK=$NET_MASK | 1;   
            $aux-=1;
        }
        else
        {
            $NET_MASK=$NET_MASK<<1;
        }
    }
    #now the real mask is ready, so i get the real net addr just in case
    $NET_ADDR=$NET_MASK & $NET_ADDR;
    $FIRST=$NET_ADDR+1;
    $LAST=$NET_ADDR+$HOSTS;
    print "NET ADDRESS: ".pickAddress($NET_ADDR).". NET MASK: ".pickAddress($NET_MASK).". HOSTS: $HOSTS. FIRST: ".pickAddress($FIRST).". LAST: ".pickAddress($LAST).".\n";

    $counter=1;

    while($counter<=$HOSTS)
    {
        $next_addr=pickAddress($NET_ADDR+$counter);
   
        $name=gethostbyaddr(inet_aton($next_addr),AF_INET);
   
        if(!$name)
        {
            $name="NONAME";
        }
   
        print $next_addr." is ".state($next_addr)." name $name\n";   
   
        $counter+=1;
    }
}
else
{#single host check
    $next_addr=pickAddress($NET_ADDR);
    $name=gethostbyaddr(inet_aton($next_addr),AF_INET);
    if(!$name)
    {
        $name="NONAME";
    }
    print $next_addr." is ".state($next_addr)." name $name\n";   
}


El script es bastante sencillo, recibe como parámetros una dirección IP y opcionalmente, el número de bits de la máscara de red. Debe ser ejecutado como root. Consiste básicamente en 2 pruebas, la primera es ver si el host responde mensajes ICMP, si los responde se concluye con que esa IP está en uso, si no responde se realiza una prueba mas en busca de puertos abiertos si la prueba devuelve algún puerto abierto, asume que la IP está en uso, de lo contrario asume que la IP está libre.

Aca les dejo una pequeña prueba. En busca de hosts en un fragmento de la red:

root@moon:/home/juan/Escritorio/stuff/Dropbox/TFG/scripts# ./chekIPs.pl 192.168.1.1 28
NET ADDRESS: 192.168.1.0. NET MASK: 255.255.255.240. HOSTS: 14. FIRST: 192.168.1.1. LAST: 192.168.1.14.
192.168.1.1 is DOWN name NONAME
192.168.1.2 is DOWN name NONAME
192.168.1.3 is DOWN name NONAME
192.168.1.4 is DOWN name NONAME
192.168.1.5 is DOWN name NONAME
192.168.1.6 is DOWN name NONAME
192.168.1.7 is DOWN name NONAME
192.168.1.8 is DOWN name NONAME
192.168.1.9 is DOWN name NONAME
192.168.1.10 is UP name NONAME
192.168.1.11 is DOWN name NONAME
192.168.1.12 is DOWN name NONAME
192.168.1.13 is DOWN name NONAME
192.168.1.14 is DOWN name NONAME


El script resulta mucho mas explícito cuando se ejecuta en una red que tiene resolución reversa de nombres.

La idea del script es meter el menor tráfico posible en la red, por eso las pruebas se separan en dos, siendo la primera muy sencilla y la segunda ya un poco mas exigente.

Yo lo uso para encontrar fácilmente direcciones IP que no están siendo utilizadas y así poder reasignarlas a nuevos hosts.

PD: si se preguntan porque está comentado en inglés (mi jefe me lo preguntó xD), no lo recuerdo jajaja.

lunes, 26 de septiembre de 2011

Ver actualizaciones Ubuntu + CentOS + OpenSuSE

Justamente en la entrada anterior veíamos una de las posibles consecuencias de no actualizar el software que utilizamos.  Si bien es cierto que actualizar indiscriminadamente puede ser un tanto riesgoso en entornos de producción, (podríamos romper alguna dependencia y dejar inútil una aplicación) actualizar ES NECESARIO!!!

Actualizar puede ser una tarea simple y hasta divertida cuando se tratan de unos pocos servidores, pero ya no tanto cuando el número comienza a ascender! Por lo tanto la idea de esta entrada es definir un mecanismo que nos permita:

-Identificar entre actualizaciones de seguridad y el resto.
-Automatizar los avisos de actualizaciones.

El modelo es el siguiente:

-Utilizamos nagios para recolectar y presentar la información.
-No buscaremos actualizaciones cada 5 minutos, las actualizaciones no salen con tanta frecuencia por lo tanto estaríamos consumiendo ancho de banda de manera innecesaria. En el esquema planteado los servidores buscaran sus actualizaciones una vez en el día, y el resto del día mostraran los datos de esa recolección hasta que sean actualizados.
-Tendremos 2 scripts:
  1. updateNombreDelSO.pl que dependerá del sistema operativo y será el que se ejecute automáticamente una vez al día para recolectar las actualizaciones disponibles. Este script generará el segundo.
  2. check_actualizaciones.sh que será el script que ejecuté nrpe en los servidores, bajo la demanda del servidor corriendo nagios. Este script es la salida del primero, solo imprime la cantidad de actualizaciones pendientes de seguridad y extra, y un código de salida acorde a la situación.
-Definimos como CRITICAL la situación en la que existen actualizaciones de seguridad sin aplicar, y como WARNING cuando solamente existen actualizaciones extras (no de seguridad) por aplicar.

updateUbuntu.pl

#!/usr/bin/perl
$STATUS_OK=0;
$STATUS_WARNING=1;
$STATUS_CRITICAL=2;
$STATUS_UNKNOWN=3;
$PATCHS=`/usr/lib/update-notifier/apt-check 2>&1`;
@A=split(';',$PATCHS);
$SALIDA="#!/bin/bash\n";
$EXIT=$STATUS_UNKNOWN;
$FILE="/usr/lib/nagios/plugins/check_actualizaciones.sh";
if($A[0] eq "0" and $A[1] eq "0")
{
    open(F,">$FILE");
    print F "$SALIDA"."echo \"Existen ".$A[0]." actualizaciones.\"\n";
    print F "exit $STATUS_OK";
    close(F);
    chmod (0777,$FILE);
    exit;
}
if($A[0] > 0)
{
    $SALIDA = $SALIDA . "echo \"ERROR - Existen ".$A[0]." actualizaciones de seguridad y ".$A[1]." extras\"\n";
    $EXIT = $STATUS_CRITICAL;
}
else
{
    $SALIDA = $SALIDA . "echo \"WARNING - Existen ".$A[1]." actualizaciones extras.\"\n";
    $EXIT = $STATUS_WARNING;
}
open(F,">$FILE");
print F $SALIDA;
print F "exit $EXIT";
close(F);
chmod (0777,$FILE);

El script es bastante, sencillo,  "/usr/lib/update-notifier/apt-check" nos devuelve una linea de la forma act_seguridad;act_extras, que separamos con split y guardamos así el número de actualizaciones de seguridad en $A[0] y el número de actualizaciones extras en $A[1]. Luego de eso simplemente definimos la salida según las actualizaciones que haya para hacer. La salida de este script es un archivo bash con el formato:

#!/bin/bash
echo "ERROR -  Existen 3 actualizaciones de seguridad y 2 extras"
exit 2

 Este archivo es el que ejecutará nrpe cada vez que nagios consulte por el plugin, por lo tanto debemos agregarlo al archivo de configuracion de nrpe:

#echo "command[check_actualizaciones]=/usr/lib/nagios/plugins/check_actualizaciones.sh">> /etc/nagios/nrpe.cfg

Ahora nos encargamos de que ĺa recolección de actualizaciones se haga una vez al día:

#echo "@daily root /path/to/script/updateUbuntu.pl" >> /etc/crontab

No olvidarse de reiniciar nrpe una vez que se agregue la linea y se ejecute manuelmente por primera vez "/path/to/script/updateUbuntu.pl".

updateCentOS.pl

Para este script necesitamos instalar un agregado de yum, ejecutar como root:  "yum -y install yum-security" .

#!/usr/bin/perl

$STATUS_OK=0;
$STATUS_WARNING=1;
$STATUS_CRITICAL=2;
$STATUS_UNKNOWN=3;

$SALIDA="#!/bin/bash\n";
$EXIT=$STATUS_UNKNOWN;
$FILE="/usr/lib/nagios/plugins/check_actualizaciones.sh";
@PATCHS=`yum -q --security check-update 2>/dev/null`;

$A[0]=0;
$A[1]=0;
foreach $i (@PATCHS)
{
    $A[0]=$A[0]+1;  
}

@PATCHS=`yum -q check-update 2>/dev/null`;
foreach $i (@PATCHS)
{
    $A[1]=$A[1]+1;  
}
if($A[0] eq "0" and $A[1] eq "0")
{
        open(F,">$FILE");
        print F "$SALIDA"."echo \"Existen ".$A[0]." actualizaciones.\"\n";
        print F "exit $STATUS_OK";
        close(F);
        chmod (0777,$FILE);
        exit;
}
if($A[0] > 0)
{
       $SALIDA = $SALIDA . "echo \"ERROR - Existen ".$A[0]." actualizaciones se seguridad y ".$A[1]." extras\"\n";
       $EXIT = $STATUS_CRITICAL;
}
else
{
        $SALIDA = $SALIDA . "echo \"WARNING - Existen ".$A[1]." actualizaciones extras.\"\n";
        $EXIT = $STATUS_WARNING;
}
open(F,">$FILE");
print F $SALIDA;
print F "exit $EXIT";
close(F);
chmod (0777,$FILE);

Este script mantiene el mismo concepto, salvo que en lugar de ejecutar un solo programa ejecuta dos, el primero obtiene las actualizaciones de seguridad disponibles y el segundo las extras.

También debemos agregar la linea en el archivo de configuración de nrpe como se explicó antes, así como también la línea en crontab para la ejecución. Reiniciar nrpe y ejecutarlo manualmente la primera vez.

updateOpenSuSE.pl

#!/usr/bin/perl

$STATUS_OK=0;
$STATUS_WARNING=1;
$STATUS_CRITICAL=2;
$STATUS_UNKNOWN=3;

@PATCHS=`zypper -q lp 2>/dev/null | grep -i needed`;
$FLAG=0;
$c=0;

$SALIDA="#!/bin/bash\n";
$EXIT=$STATUS_UNKNOWN;
$FILE="/usr/lib/nagios/plugins/check_actualizaciones.sh";

foreach $i (@PATCHS)
{
    $c=$c+1;
    @aux=split('\|',$i);
    #solo uso los campos 1, 2 y 3
    if($i =~ /security/)
    {
        push(@SEGURIDAD,$aux[1]);
    }
    else
    {
        push(@OTROS,$aux[1]);
    }
    $FLAG=1;
}

if(@SEGURIDAD == 0 and @OTROS == 0)
{
        open(F,">$FILE");
        print F "$SALIDA"."echo \"Existen ".@SEGURIDAD." actualizaciones.\"\n";
        print F "exit $STATUS_OK";
        close(F);
        chmod (0777,$FILE);
        exit;
}

if(@SEGURIDAD > 0)
{
        $SALIDA = $SALIDA . "echo \"ERROR - Existen ".@SEGURIDAD." actualizaciones de seguridad y ".@OTROS." extras\"\n";
        $EXIT = $STATUS_CRITICAL;
}
else
{
        $SALIDA = $SALIDA . "echo \"WARNING - Existen ".@OTROS." actualizaciones extras.\"\n";
        $EXIT = $STATUS_WARNING;
}

open(F,">$FILE");
print F $SALIDA;
print F "exit $EXIT";
close(F);
chmod (0777,$FILE);


Nuevamente pero esta vez para openSuSE, también se deben aplicar los puntos de los otros dos casos.

No voy a incluir la configuración de nagios (por tiempo, ya me dio sueño :P, si alguien la precisa la tengo), pero básicamente consiste en definir un servicio y utilizar nrpe para ejecutar remotamente "check_actualizaciones.sh", de esta forma nagios nos estaría avisando cuántas actualizaciones pendientes tenemos y de qué tipo son.

Cabe aclarar que si actualizamos un servidor, las alertas no se irán hasta que no se vuelva a hacer la recolección, pero como esto pasa una vez al día estaríamos viendo alertas el resto del día. Por lo tanto recomiendo que una vez actualizado el servidor se ejecute manualmente el archivo de recolección para que las alarmas se vayan :D.

Concejos, dudas y demás serán bien recibidos!