Comprobar automáticamente la dirección IP pública de nuestra Raspberry Pi.

Hoy quiero explicaros cómo programar un script en Python para comprobar automáticamente la dirección IP pública de nuestra Raspberry Pi (y de todos los dispositivos de nuestra red WiFi), y hacer que nos informe por correo electrónico si la dirección IP ha cambiado. También te explicaré cómo configurarla para que ejecute el script automáticamente cada cierto tiempo.

(En realidad, este tutorial no solamente sirve para Raspberry Pi, sino que es útil en cualquier distribución Linux).

¿Por qué es útil esto?

Pues porque ahora que ya hemos aprendido cómo controlar nuestra Raspberry Pi mediante SSH y hemos configurado un servidor web en nuestra Raspberry Pi, ahora sería muy útil poder acceder a ella desde cualquier sitio, ya sea por SSH o porque queremos ver la web que tengamos alojada en nuestro servidor web.

¿Y cuál es el problema?

Con redireccionar el puerto 22 de nuestro router hacia la dirección IP privada de nuestra Raspberry Pi para las conexiones SSH y el puerto 80 para las conexiones con el servidor web sería suficiente, pensará más de uno. Por supuesto, y eso es necesario si queremos acceder desde fuera de nuestra red a nuestra Raspberry Pi. El problema está en que rara vez (muy rara vez) contaremos con una dirección IP pública estática, es decir, nuestra dirección IP pública variará cada cierto tiempo, por lo que, por mucho que sepamos nuestra dirección IP pública, si ésta cambia cuando estamos fuera de casa y, sin saber que ha cambiado, intentamos acceder a nuestra Raspberry desde fuera, nos encontraremos con que no podemos.

Algunos conceptos básicos de redes.

Para aquellos que no lo sepan, una de las clasificaciones que se le puede aplicar a las direcciones IP es la de “direcciones públicas” y “direcciones privadas”. Hubo un momento en el que, debido a la rápida expansión de Internet, el número de direcciones IP disponibles para asignar a los diferentes dispositivos conectados a Internet estaba cercano a agotarse por cuestiones matemáticas. Por ello, decidieron reservar un rango de esas direcciones como “direcciones IP privadas”, otras tantas como “direcciones IP públicas” y otras tantas para otro tipo de direcciones IP que ahora mismo no nos interesan.

La diferencia entre una dirección IP pública y una dirección IP privada es, básicamente, desde dónde podemos acceder a ellas. Me explico: todo dispositivo conectado a una red WiFi normal, de la que todos tenemos en casa, tiene una dirección IP privada. Cada dispositivo la suya. Sin embargo, a esa dirección IP solamente se puede acceder desde dentro de la propia red. Si te vas a casa de tu amigo y tratas de acceder al ordenador de tu casa poniendo su dirección IP privada, no podrás acceder a él. De hecho, en la red de tu amigo seguramente esa dirección IP corresponderá a uno de los dispositivos que tenga en su casa conectados a su red. Sin embargo, a una dirección IP pública se puede acceder desde cualquier lugar del mundo y, en el caso de las redes WiFi de andar por casa, suele corresponder al router WiFi.

Un router WiFi tiene, como mínimo, dos direcciones IP diferentes, de las cuales una es sí o sí, pública. Esta dirección IP es la que nuestro router usa para comunicarse con el resto de dispositivos de Internet. Las otras direcciones IP del router suelen ser privadas, y son las que el router usa para comunicarse con los dispositivos y subredes que haya dentro de su propia red. De esta forma, cualquier comunicación que haga hacia Internet un dispositivo conectado a una red, llevará como dirección IP de origen la dirección IP pública del router, independientemente del dispositivo que sea y de cual sea su dirección IP privada.

Además, como ya he mencionado antes, rara vez nos encontraremos con que nuestra dirección IP pública se mantenga invariable. Aunque no suele cambiar habitualmente, sí puede pasar cada cierto tiempo, y esto puede ocasionarnos algún problema si queremos acceder a nuestra Raspberry y la dirección ha cambiado sin que lo sepamos. Por ello nos interesa conocer en todo momento cuál es nuestra dirección IP pública, y para eso te va a ser de gran ayuda este script.

Script “checkPublicIP.py”

Para solucionar este pequeño problema, me dispuse a escribir un pequeño script en Python que lo que hace es obtener la dirección IP pública del router al que está conectada la Raspberry, compararla con la que tenemos almacenada en un pequeño fichero de texto y, si ha variado, sustituir la antigua por la nueva, y enviarnos la nueva por correo electrónico. El script completo es el siguiente:

#!/usr/bin/python
# -*- coding: utf-8 -*-

# Comprobación de la IP pública y envío de correo si cambia
# www.manusoft.es

# importamos la librería necesaria para el envío de correos
import smtplib
# importamos la librería necesaria para hacer una consulta HTTP
import urllib
# importamos la librería necesaria para acceder a un fichero
import os.path as path

# Apertura del fichero con la última dirección IP pública registrada 
# Comprobamos que el fichero existe 
if path.exists('publicIPs.txt'):
        # Si el fichero existe lo abrimos en modo lectura. El atributo 'r' indica que es de solo lectura 
        archivo = open("publicIPs.txt", 'r')
else:
        # Si el archivo no existe lo creamos en modo lectura y escritura. El atributo 'w+' abre el fichero en modo lectura y escritura y, si no existe, lo crea 
        archivo = open("publicIPs.txt", 'w+')
# Obtención de la dirección IP pública actual
publicIpActual = urllib.urlopen('http://icanhazip.com').read()

# Lectura de la última dirección IP pública registrada desde el fichero
# Se leen todas las líneas del fichero quedándonos únicamente con la última
linea = archivo.readline()
publicIp = ""
while linea != '':
        publicIp = linea
        linea = archivo.readline()

# Cierre del fichero con la última dirección IP pública registrada
archivo.close()

# Si en el archivo no existe ninguna dirección IP pública registrada, o la última registrada es diferente a la actual
if publicIp == "" or publicIp != publicIpActual:
        # Guarda la dirección IP pública actual en la variable "publicIP"
        publicIp = publicIpActual
        # Escribe la dirección IP pública actual en el fichero "publicIPs.txt". El atributo 'w' abre el fichero únicamente en modo escritura y, si no existe, lo crea. Este modo s$
        archivo = open("publicIPs.txt", 'w')
        archivo.write(publicIp)
        # Cierre del fichero
        archivo.close()

        # Se envía la dirección IP pública actual por correo electrónico
        # Variable con la dirección de correo electrónico de origen
        fromAddr = '***************@gmail.com'
        # Variable con la dirección de correo electrónico de destino
        toAddr = '***************'
        # Variable con el asunto del correo electrónico
        subject = 'New public IP address'
        # Variable con el mensaje.
        msg = 'La nueva direccion IP publica es ' + publicIp
        # Variable con el correo electrónico completo
        email = """From: %s
To: %s
MIME-Version: 1.0
Content-type: text/html
Subject: %s
%s
""" %(fromAddr, toAddr, subject, msg)

        # Variable con el usuario de la dirección de correo electrónico de origen
        username = '**************@gmail.com'
        # Variable con la contraseña de la dirección de correo electrónico de origen
        password = '**********'

        # Envío del correo
        # Apertura del servidor SMTP en el servidor de correo electrónico de origen
        try:
                server = smtplib.SMTP('smtp.gmail.com:587')
                server.starttls()
                try:
                        # Inicio de sesión en el servidor de correo electrónico de origen
                        server.login(username, password)
                        try:
                                # Envío del correo electrónico
                                server.sendmail(fromAddr, toAddr, email)
                        except:
                                print('Error al enviar el correo electrónico')
                except:
                        print('Error al iniciar sesión en GMail')
                # Cierre del servidor SMTP
                server.quit()
        except:
                print('Error al iniciar el servidor SMTP')

### FIN DEL SCRIPT ###

El script está completamente auto-explicado gracias a los comentarios (para los nuevos en programación, que alguno habrá, los comentarios son las partes del texto de color gris que está tras los símbolos “#”). Únicamente debes tener en cuenta un par de cosas: la primera, que está configurado para que correo electrónico se envíe desde una cuenta de correo de GMail, y la segunda, que debes sustituir los asteriscos por tus datos de correo, es decir, por la dirección de correo electrónico desde la que se va a enviar el correo, la contraseña de esa misma cuenta de correo y por la dirección de correo electrónico que quieres que reciba el correo electrónico.

Para escribir este script en nuestra Raspberry Pi no tenemos más que conectarnos a ella mediante SSH (o abrir un terminal si la tenemos conectada a una pantalla y a un teclado) y movernos al directorio donde queramos guardar el script. Para moverte por los directorios de la Raspberry debes usar el comando “cd”. Por ejemplo, si queremos movernos al directorio “Python_Projects” que se encuentra dentro de “Documents”, ejecutaremos el siguiente comando:

cd Documents/Python_Projects

Y ahora, ejecutamos el siguiente comando:

nano checkPublicIp.py

Puedes sustituir “checkPublicIp” por el nombre que quieras darle al script. Este comando nos va a abrir un script de Python en blanco. En él escribiremos el código que hemos visto antes. Tras escribir el código, guardamos el fichero dejando pulsada la tecla CTRL y pulsando la X.

Y ya, si quieres, puedes probar que funciona. Para probar el script debes ejecutar el siguiente comando sin moverte del directorio en el que estamos:

python checkPublicIp.py

Debes cambiar “checkPublicIp” por el nombre que le diste al script cuando lo creaste. Al ejecutar el comando, tras un instante en el que la Raspberry se queda un poco parada mientras procesa el script, veremos que aparentemente no ha pasado nada. Sin embargo, si miras en la bandeja de entrada (o en la carpeta de spam) de la dirección de correo electrónico que pusiste como destino en el script, verás que hay un correo electrónico indicándote tu dirección IP pública. Además, habrá un nuevo fichero de texto en el directorio donde está el script, llamado “publicIPs.txt”. Este fichero contiene la dirección IP pública actual de tu router, y es el que el script usará para comprobar si ha cambiado desde la última vez que se ejecutó.

Configurando la ejecución automática del script.

Ya tenemos nuestro script, y sabemos que funciona. Ahora tenemos que indicarle a la Raspberry Pi que lo ejecute periódicamente por su cuenta. Para ello haremos uso del demonio “cron”.

¿Qué es cron?

Según el blog DesdeLinux, cron es un administrador regular de procesos en segundo plano (demonio) que ejecuta procesos o guiones a intervalos regulares (por ejemplo, cada minuto, día, semana o mes). Los procesos que deben ejecutarse y la hora en la que deben hacerlo se especifican en el fichero crontab.

Automatizar el script con cron.

Para automatizar el script con cron en nuestra Raspberry debemos acceder al fichero crontab. Para ello, escribimos el siguiente comando:

crontab -e

Si es la primera vez que ejecutamos este comando, puede que la consola nos devuelva algo así:

no crontab for pi - using an empty one

Select an editor. To change later, run 'select-editor'

1. /bin/ed
2. /bin/nano <----- easiest
3. /usr/bin/vim.tiny

Choose 1-3 []:

Escogemos el editor que más nos guste. El que hemos estado usando durante este tutorial, y mi favorito, es nano, por lo que tecleamos un 2 y pulsamos Enter. Ahora veremos algo así:

# Edit this file to introduce tasks to be run by cron.
#
# Each task to run has to be defined through a single line
# indicating with different fields when the task will be run
# and what command to run for the task
#
# To define the time you can provide concrete values for
# minute (m), hour (h), day of month (dom), month (mon),
# and day of week (dow) or use '*' in these fields (for 'any').#
# Notice that tasks will be started based on the cron's system
# daemon's notion of time and timezones.
#
# Output of the crontab jobs (including errors) is sent through
# email to the user the crontab file belongs to (unless redirected).
#
# For example, you can run a backup of all your user accounts
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
#
# For more information see the manual pages of crontab(5) and cron(8)
#
# m h  dom mon dow   command

Esto significa que ya hemos conseguido acceder al fichero crontab. Ahora vamos a configurar la ejecución automática del script. Para ello, debemos añadir al final del fichero la siguiente línea de texto:

0 0 * * *  cd ~/Documents/Python_Projects && python checkPublicIp.py

Esta línea le indica a cron que debe moverse hasta la ruta “~/Documents/Python_Projects” y ejecutar el script llamado “checkPublicIp.py” a las 0:00h cada día del mes, cada mes y sea cual sea el día de la semana.

El primer 0 indica el minuto en el que debe ejecutarse el script. Podría ser también un asterisco si quisiéramos que se ejecutara a cada minuto. El segundo 0 indica la hora a la que debe ejecutarse el script. Es decir, el script se ejecutaría cada minuto desde las 0:00h hasta las 0:59h si en lugar del primer 0 hubiera un asterisco. Los siguientes tres valores, que en nuestro caso hemos marcado con asterisco son, de izquierda a derecha, el día del mes, el mes (del 1 al 12) y el día de la semana (del 1 al 7). Si quieres profundizar un poco en la configuración del fichero crontab, mira este enlace.

Y para terminar, reiniciamos el servicio de cron ejecutando el siguiente comando:

sudo service cron restart

¡Y nada más! De esta forma ya tienes configurado tu script para que se ejecute correctamente y compruebe cada día a las 12 de la noche si la dirección IP pública de nuestro router ha cambiado y, si lo ha hecho, te avise por correo electrónico. Si quiere probar ahora mismo si funciona, puedes cambiar el momento en el que cron ejecute el script por un momento más próximo y así te aseguras que todo ha ido bien.

Como siempre, cualquier sugerencia o consulta podéis hacérmela llegar enviando un correo electrónico a sugerencias@manusoft.es o dejando un comentario en la entrada. ¡Muchas gracias por visitar ManuSoft.es!