Para ello lo primero que tenemos que tener en cuenta es donde podemos "interponer" nuestro segundo factor de autenticación... Es decir en qué parte del sistema podemos aplicar nuestro programa para que tanto los logins locales como remotos se vean afectados... El lugar más apropiado sería el fichero .bashrc alojado en la carpeta "home" de cada usuario, pues sabemos con toda seguridad que cualquier inicio de sesión ejecutará dicho programa y que dicho fichero es fácilmente editable... En mi caso en particular he aplicado el segundo factor de autenticación en el fichero /home/ivan/.bashrc; fichero al que le habría añadido está línea al final:
/usr/bin/2fa.sh
Esta línea simplemente significaría que, tras realizar todas las tareas correspondientes, ejecute el script 2fa.sh, script que de momento no existe (o que no debería existir) ya que dicho fichero va a ser fabricación nuestra. Empecemos a construir nuestro script paso a paso... Lo primero que tenemos que hacer es generar un código aleatorio que podamos usar en nuestra "nueva" autenticación... Con un código numérico tendría que ser suficiente, así que vamos a comenzar nuestro script con una pequeña función que genere un número aleatorio de 6 dígitos, si bien podemos hacer que dicho número sea mayor en caso de verlo necesario. Podemos escribir nuestro script con el editor que más cómodo nos sea, vi, nano, emacs... Lo importante sería que dicho script se llamase 2fa.sh y se alojase en /usr/bin. Además dicho script tendría que ser creado como root. Nuestro comienzo sería el siguiente:
- #!/bin/bash
- #VARIABLES
- CLAVE=""
- #GENERADOR NUMERO ALEATORIO
- function generador_clave()
- {
- for i in `seq 1 6`;
- do
- rand=$(( $RANDOM % 10 ))
- CLAVE="${CLAVE}${rand}"
- done
- }
- generador_clave
Este comienzo como podéis ver es tan simple como crear una variable "vacía" llamada CLAVE. Luego crearíamos una función que asignaría 6 dígitos aleatorios a dicha variable y por último llamaríamos a la función. Algo muy sencillo y que de momento no entraña ninguna dificultad...
Esta clave sería la que luego tendríamos que usarla para autenticarnos con lo que es importante hacérnosla llegar... Aquí podemos pensar en diferentes métodos; algunos más laboriosos que otros, tales como el uso de un router GSM que nos envíe mensajes a nuestro móvil... Pero en este caso lo que queremos es una opción sencilla y que no requiera ningún tipo de inversión o de aparato "especial", con lo que en mi caso en particular me he decantado por enviar la clave por correo electrónico. Para ello habría que usar la aplicación sendemail; aplicación que no viene instalada por defecto en el sistema con lo que en caso de no tenerla instalada habría que instalarla vía consola mediante el comando:
apt-get install sendemail
Con esta aplicación instalada, antes de usarla en nuestro script es importante entender su funcionamiento... Esta aplicación lo que hace es que nos conectemos a una cuenta de correo mediante el protocolo SMTP, para después desde dicha cuenta de correo enviar una mail a un destinatario; acción para lo cual se usaría la siguiente sintaxis:
sendemail -f mail_origen -t mail_destino -xu "mail_origen" -xp "contraseña" -m "Contenido_mensaje" -s servidor_smtp:puerto -o tls=yes -u "Título_mail"
Aquí lo único "especial" sería el servidor SMTP y el puerto a usar, ya que ambos datos variarían dependiendo de si el servidor es de un dominio u otro. Los correos más populares serían gmail y hotmail así que he aquí a continuación la lista de servidores SMTP de dichos servicios de correo.
Hotmail
Servidor: smtp.live.com
Puerto: 25
Gmail
Servidor: smtp.gmail.com
Puerto: 465
A sabiendas del comando y de la información de los servidores SMTP, podemos ahora darle más forma a nuestro script y darle la funcionalidad de enviarnos por correo la clave previamente generada, haciendo que tome el siguiente aspecto:
- #!/bin/bash
- #VARIABLES
- CLAVE=""
- #ACTIVACION TRAP
- trap '' 2 3 9 15
- #GENERADOR NUMERO ALEATORIO
- function generador_clave()
- {
- for i in `seq 1 6`;
- do
- rand=$(( $RANDOM % 10 ))
- CLAVE="${CLAVE}${rand}"
- done
- }
- #ENVIADOR CORREO
- function enviador_mail()
- {
- sendemail -f usuario@gmail.com -t destino@hotmail.com -xu "[email protected]" -xp "Contraseña" -m "${CLAVE}" -s smtp.gmail.com:465 -o tls=yes -u "2FA" > /dev/null
- }
- #LLAMADA FUNCIONES
- generador_clave
- enviador_mail
Con lo que tenemos ahora ya estaríamos mandando una clave aleatoria de 6 dígitos desde un correo de origen a un destino... Lo cual esta muy bien y se va acercando a lo que buscamos, pero que todavía no se trata de lo que buscamos, pues no se hace nada con dicha clave...
Ahora llegaría el turno de hacer que el usuario pueda introducir una clave manualmente y que ésta sea comparada con la clave que hemos enviado por correo. Aquí podemos optar por diferentes métodos de introducción de contraseña dependiendo de cómo nos logueemos; no es lo mismo loguearnos por consola que vía entorno gráfico, con lo que tendremos que hacer un script que se pueda adaptar a la situación dependiendo de nuestras necesidades. Para este script en concreto haremos que se pueda introducir la clave tanto por consola, como en entornos gráficos Gnome y KDE. Eso significaría que usaríamos 3 métodos de introducción de datos distintos:
- Usaríamos el comando read para leer el dato introducido desde la consola. El comando sería read PASS (PASS el nombre de la variable que usaríamos para almacenar la contraseña).
- Para la introducción de la contraseña mediante una interfaz gráfica, primero habría que ver si el entorno gráfico es gnome o KDE. En caso de ser KDE habría que usar la herramienta kdialog que usaría la sintaxis:
- Mientras que en Gnome la herramienta sería zenity, y la sintaxis sería esta:
Con estas introducciones haríamos que la variable PASS tuviese el valor introducido; variable que compararíamos con la clave. Obviamente haríamos que estas introducciones apareciesen constantemente hasta que la clave introducida coincida con la clave.
Con lo que recién acabamos de comentar, el script tomaría el siguiente aspecto, el cual como podéis ver, es mucho más completo. Un script que con lo que aparece a continuación ya sería funcional:
- #!/bin/bash
- #VARIABLES
- CLAVE=""
- PASS=""
- #GENERADOR NUMERO ALEATORIO
- function generador_clave()
- {
- for i in `seq 1 6`;
- do
- rand=$(( $RANDOM % 10 ))
- CLAVE="${CLAVE}${rand}"
- done
- }
- #ENVIADOR CORREO
- function enviador_mail()
- {
- sendemail -f usuario@gmail.com -t destino@hotmail.com -xu "[email protected]" -xp "Contraseña" -m "${CLAVE}" -s smtp.gmail.com:465 -o tls=yes -u "2FA" > /dev/null
- }
- #LLAMADA FUNCIONES
- generador_clave
- enviador_mail
- #DIALOGS PARA ENTORNO GRAFICO
- if [[ "$(env |grep DISPLAY)" = "DISPLAY=:0" ]]; then
- while [ "${CLAVE}" != "${PASS}" ]; do
- if [[ "$(env |grep XDG_CURRENT_DESKTOP)" = "KDE" ]]; then #DIALOG PARA KDE
- PASS=$(kdialog --title "PASSWORD" --inputbox "Introduce la contraseña")
- else #DIALOG PARA GNOME
- PASS=$(zenity --entry --title="PASSWORD" --text="Introduce la contraseña")
- fi
- done
- #TEXTO PLANO PARA CONSOLA
- else
- while [ "${CLAVE}" != "${PASS}" ]; do
- echo 'Introduce la contraseña'
- read PASS
- done
- fi
Gracias a este script compararíamos la contraseña introducida (ya sea por consola o por interfaz gráfica) y haríamos que no dejase de pedir la contraseña hasta que sea la correcta... Es decir que el script en sí cumpliría su función, si bien todavía habría que mejorarlo un poco más, pues tiene pequeñas carencias...
La primera de ellas sería que cada vez que un usuario se loguee para lo que sea, pedirán el segundo factor de autenticación, aún cuando sea el mismo y simplemente haya abierto otra consola... Lo suyo sería que durante el tiempo que el equipo estuviese encendido, no volviese a pedir el segundo factor de autenticación, pues sino a la mínima tendríamos al sistema pidiéndonos una nueva clave de forma innecesaria, pues se da por hecho que si has pasado exitosamente la primera vez por dicho método, no hay necesidad de repetir dicha autenticación. Para ello simplemente habría que crear un fichero en el directorio temporal /tmp (que sabemos que vacía su contenido al reiniciar el equipo) cuya existencia se comprobaría dentro del fichero .bashrc para así saber si ejecutar el script o no. En mi caso he denominado a estos ficheros temporales token_${UID}, donde $UID sería el identificador del usuario. Por defecto dicho fichero no existiría, con lo que la primera vez siempre habría que crear dicho fichero mediante el script. Es por eso que en el fichero .bashrc haríamos que el script se ejecutase si el fichero NO existiese, lo cual ser haría modificando la última línea del dicho fichero para que tenga este aspecto:
- if [ ! -f /tmp/token_${UID}] ; then
- /usr/bin/2fa.sh
- fi
Además, en el estado actual el script es bastante vulnerable; un simple ctrl + c en un login por consola lo detendría y haría que se saltasen este paso sin problemas... Es por ello que además del cambio atrás mencionado vamos a "atrapar" algunas señales para evitar esta situación mediante el comando trap; en concreto las señales 2 3 9 y 15. Obviamente, al finalizar el script deshabilitaríamos dicho "atrape".
Ambas modificaciones, tanto la evasión de la repetición, como la captura de ciertas señales harían que el script 2fa.sh tomase este aspecto:
- #!/bin/bash
- #VARIABLES
- CLAVE=""
- PASS=""
- #ACTIVACION TRAP
- trap '' 2 3 9 15
- #GENERADOR NUMERO ALEATORIO
- function generador_clave()
- {
- for i in `seq 1 6`;
- do
- rand=$(( $RANDOM % 10 ))
- CLAVE="${CLAVE}${rand}"
- done
- }
- #ENVIADOR CORREO
- function enviador_mail()
- {
- sendemail -f usuario@gmail.com -t destino@hotmail.com -xu "[email protected]" -xp "Contraseña" -m "${CLAVE}" -s smtp.gmail.com:465 -o tls=yes -u "2FA" > /dev/null
- }
- #LLAMADA FUNCIONES
- generador_clave
- enviador_mail
- #DIALOGS PARA ENTORNO GRAFICO
- if [[ "$(env |grep DISPLAY)" = "DISPLAY=:0" ]]; then
- while [ "${CLAVE}" != "${PASS}" ]; do
- if [[ "$(env |grep XDG_CURRENT_DESKTOP)" = "KDE" ]]; then #DIALOG PARA KDE
- PASS=$(kdialog --title "PASSWORD" --inputbox "Introduce la contraseña")
- else #DIALOG PARA GNOME
- PASS=$(zenity --entry --title="PASSWORD" --text="Introduce la contraseña")
- fi
- done
- #TEXTO PLANO PARA CONSOLA
- else
- while [ "${CLAVE}" != "${PASS}" ]; do
- echo 'Introduce la contraseña'
- read PASS
- done
- fi
- #EVITAR REPETICION 2FA PARA UN MISMO USUARIO HASTA REINICIO
- if [ "${CLAVE}" = "${PASS}" ]; then
- touch /tmp/token_${UID}
- chmod 400 /tmp/token_${UID}
- fi
- #DESACTIVACION TRAP
- trap - 2 3 9 15
Ahora sí que tendríamos un script que no solo es funcional, sino que también puede usarse en situaciones reales... Un script que añadiría una capa de seguridad adicional a nuestro sistema y que únicamente requiere añadir la llamada al script /usr/bin/2fa.sh en el fichero .bashrc de cada usuario que queremos que tenga doble seguridad. Solo quedaría por hacer una cosa más en nuestro script, que sería opcional y dependería de si tenemos varios usuarios usando el mismo equipo (con diferentes cuentas de usuario, se entiende). Este script requiere especificar a mano todos los datos de mail, el origen, el destino, usuario, contraseña... Al ser un binario que va a ser usado por todos los usuarios, tendríamos que ver si nos merece la pena o no poner o no diferentes configuraciones de correo dependiendo del usuario...
En caso de querer tener flexibilidad con dicho dato, el cual es bastante personal y que no queremos que el resto de usuarios vea... Podemos hacer que cada usuario tenga una configuración especial y que en caso de que no haya configuración especial alguna para dicho usuario, se use un correo genérico. Esto se podría lograr gracias a la privacidad que nos ofrece la carpeta home. Cada usuario tiene una carpeta /home/nombre_usuario a la que solamente pueden acceder ellos y root con lo que podemos crear un fichero con un nombre y una estructura determinada que sirva para extraer los datos de allí y usarlos. Por ejemplo podríamos crear un fichero llamado llamado User2fa dentro de nuestro directorio home (por ejemplo dentro de /home/ivan en mi caso) que tuviese la estructura:
- DE: [email protected]
- A: [email protected]
- USUARIO: "[email protected]"
- PWD: "Contraseña"
- SERVIDOR: smtp.gmail.com
- PUERTO: 465
Con estos datos, simplemente habría que recorrer el fichero y recoger los valores para luego ponerlos en la posición indicada del script... Algo tan sencillo como editar la función enviador_mail y hacer que el aspecto final sea este:
- #!/bin/bash
- #VARIABLES
- CLAVE=""
- PASS=""
- #ACTIVACION TRAP
- trap '' 2 3 9 15
- #GENERADOR NUMERO ALEATORIO
- function generador_clave()
- {
- for i in `seq 1 6`;
- do
- rand=$(( $RANDOM % 10 ))
- CLAVE="${CLAVE}${rand}"
- done
- }
- #ENVIADOR CORREO
- function enviador_mail()
- {
- # SI EXISTE EL fichero User2fa, usamos los datos de este
- if [ -f ~/User2fa ]; then
- i=1
- while [ ${i} -le 6 ]; do
- VAR[${i}]="$(head -n ${i} ~/User2fa |tail -n -1 |awk '{print $2}')"
- i=$(($i + 1))
- done
- sendemail -f ${VAR[1]} -t ${VAR[2]} -xu "${VAR[3]}" -xp "${VAR[4]}" -m "${CLAVE}" -s ${VAR[5]}:${VAR[6]} -o tls=yes -u "2FA" >/dev/null
- else # EN CASO CONTRARIO, USAMOS UNOS DATOS GENERICOS
- sendemail -f usuario@gmail.com -t destino@hotmail.com -xu "[email protected]" -xp "Contraseña" -m "${CLAVE}" -s smtp.gmail.com:465 -otls=yes -u "2FA" > /dev/null
- fi
- }
- #LLAMADA FUNCIONES
- generador_clave
- enviador_mail
- #DIALOGS PARA ENTORNO GRAFICO
- if [[ "$(env |grep DISPLAY)" = "DISPLAY=:0" ]]; then
- while [ "${CLAVE}" != "${PASS}" ]; do
- if [[ "$(env |grep XDG_CURRENT_DESKTOP)" = "KDE" ]]; then #DIALOG PARA KDE
- PASS=$(kdialog --title "PASSWORD" --inputbox "Introduce la contraseña")
- else #DIALOG PARA GNOME
- PASS=$(zenity --entry --title="PASSWORD" --text="Introduce la contraseña")
- fi
- done
- #TEXTO PLANO PARA CONSOLA
- else
- while [ "${CLAVE}" != "${PASS}" ]; do
- echo 'Introduce la contraseña'
- read PASS
- done
- fi
- #EVITAR REPETICION 2FA PARA UN MISMO USUARIO HASTA REINICIO
- if [ "${CLAVE}" = "${PASS}" ]; then
- touch /tmp/token_${UID}
- chmod 400 /tmp/token_${UID}
- fi
- #DESACTIVACION TRAP
- trap - 2 3 9 15
Gracias a estos cambios finales realizados no solo tendríamos un segundo factor de autenticación sino que además tendríamos unos datos de mail adaptados a las necesidades de cada usuario, ofreciendo la flexibilidad de que cada usuario que use el segundo factor de autenticación pueda escoger el mail de origen y de destino a su completo gusto.
Espero que os haya resultado útil.
Saludos.