Implementación de SSH con Docker

Como se describe en el documento los fundamentos de SSH, tenemos un escenario donde no queremos exponer un servidor de base de datos. Las razones por las cuales nos queremos conectar al servidor es para realizar tareas administrativas como crear un usuario, modificar los permisos, o crear objetos nuevos. Cuando este requerimiento es necesario, entonces podemos establecer un redireccionamiento de puerto o entunelamiento SSH, como se conoce. En este caso, lo necesitamos para conectarnos a un servicio en una red interna desde el exterior (internet).

En el diagrama a continuación se muestra en el recuadro la parte que vamos a configurar. Es válido mencionar que aunque se está simulando una red LAN, perfectamente se puede hacer para una red WAN.

Figura 1. Diagrama de red LAN básico con SSH.

Instalación y configuración de SSH

El servicio SSH se puede configurar de dos maneras: por medio de usuario/contraseña o con utilización de llaves. Se puede configurar de la primera manera y luego seguir con la segunda, de modo que si todo funciona correctamente entonces podemos deshabilitar la autenticación por contraseña.

Configuración con contraseña

La configuración con contraseña es sencilla, solo debemos instalar OpenSSH, activar el servicio y asegurarnos que se puede recibir conexiones en el puerto destinado, TCP 22. Primero, debemos ejecutar un contenedor donde podamos instalar y configurar SSH. He creado una imagen personalizada que contiene lo siguiente:

  • OpenSSH instalado
  • Usuario admin creado con los permisos correspondientes
  • Configuración para que pueda ejecutar comandos
  • Configuración del puerto de conexión SSH

Podemos ejecutar una instancia (en modo interactivo) basado en la imagen con el siguiente comando:

docker run --rm -it --name ssh_mysql --hostname ssh_mysql hcastelloncom/ssh:v1.1 sh

Con el prompt del contenedor, podemos verificar que SSH está instalado utilizando el comando which y le pasamos el nombre del daemon:

which sshd

El resultado debería ser la ruta del daemon tal como se puede observar en la figura 2:

Figura 2. Verificación SSH está instalado.

Ahora podemos averiguar el estado del servicio con el comando service que ejecuta un script del System V init. service ssh status

Si encontramos que el servicio no está corriendo, tal como se muestra en la <u>figura 3</u>, entonces podemos utilizar el mismo comando para iniciarlo, cambiando la orden status por start

service ssh start
Figura 3. Verificación estado de SSH e iniciación del servicio.

Verificación del servicio

Antes de configurar SSH para que utilice llaves, debemos verificar que hasta el momento funciona correctamente. Para ello, vamos a instanciar un cliente ssh para que se conecte al contenedor del paso anterior. Vamos a utilizar Alpine de la siguiente manera:

docker run -it --rm --name sshclient alpine sh

Luego de ejecutar el comando, observamos que estamos en el contenedor por el prompt. Actualizamos los paquetes de Alpine e instalamos openssh, que nos da acceso al cliente ssh con los siguientes comandos:

apk update && apk add openssh

Ahora bien, estamos configurando solo el servicio SSH entre un cliente y un servidor. Por tanto, necesitamos conocer la dirección IP del servidor para realizar la prueba. La dirección IP del servidor la podemos obtener con el comando docker container inspect nombre_contenedor y buscar el valor para IPAddress. Una vez que tenemos la dirección IP, entonces podemos comprobar que tenemos conectividad con el servidor con la herramienta ping

ping 172.17.0.2

El resultado del comando debería ser similar al siguiente:

Figura 4. Verificación estado de SSH e iniciación del servicio.

Sólo resta establecer la conexión al servidor. La sintaxis del comando es ssh nombre_usuario@host, donde el host puede ser una dirección o un nombre de dominio, tal como se muestra a continuación:

ssh admin@172.17.0.2

Luego de escribir la contraseña, nos debería mostrar la consola de la instancia remota en la cual podemos ejecutar comandos o realizar tareas.

Figura 5. Verificación de conexión SSH al servidor.

En la segunda parte de este serie, estaremos configurando el servicio para que utilice llaves en lugar de contraseña.

Implementación servidor DHCP (contenedor)

Introducción

En este post vamos a presentar una manera rápida y sencilla de cómo implementar un servidor DHCP Linux en un contenedor y probar su funcionalidad con un cliente. El Protocolo de Configuracion de Host Dinámico (o DHCP) permite asignar no solo direcciones IP a dispositivos en la red si no otra información necesaria para que se pueda comunicar con otros dispositivos utilizando diferentes servicios como DNS, Directorio Activo, impresión, etc. El objetivo de este post es para fines educacionales o pruebas de concepto (POC).

Escenario

Antes de pasar a la implementación es necesario presentar en diagrama de implementación. La idea del diagrama es que podamos entender lo que queremos hacer y cómo. Para implementar el servicio DHCP en un contenedor debemos trabajar un escenario mixto entre contenedor y máquina virtual. Lo anterior es porque no podemos decirle a un contenedor que se conectará a una red hasta que se esté ejecutando, si no que tiene que ser incluso antes de que exista. Por tal razón, vamos a crear un contenedor con el servicio DHCP y lo vamos a conectar a la red host, de modo que no haya aislamiento entre el contenedor y el host (Docker). En lugar de ello, el contenedor será una extensión del host porque tendrá el mismo stack de red que éste. Para el rol de cliente, lo que hacemos es crear una máquina virtual que esté conectada a una de las redes (sólo anfitrión). El escenario lo podemos observar en la siguiente imagen:

Figura 1. Escenario de implementación servidor DHCP Linux.

Requerimientos iniciales

Cliente

Del lado del cliente, debemos preparar una máquina virtual de modo que uno de los adaptadores de red esté conectado a una de las redes denominadas Host only Ethernet adapter tal como lo muestra la imagen a continuación:

Figura 2. Configuración adaptador de red de la máquina virtual cliente DHCP.

El adaptador que esté asignado a la máquina virtual debe estar configurado para no tomar direccionamiento dinámico del servicio DHCP integrado en virtualbox. Para confirmarlo, nos vamos a archivo >> administrador de redes del anfitrión y verificar que esté deshabilitado el servidor DHCP. Como es de esperarse, antes de realizar estas configuraciones deberá haber creado la máquina virtual.

Figura 3. Configuración de la red ‘host-only’.

Servidor DHCP

Lo siguiente es instalar y configurar el servidor DHCP. Lo que haremos primero es crear una instancia de contenedor basada en la imagen de Alpine. Para ello, ejecutamos el siguiente comando:

docker run --rm -it --name servidor_dhcp --hostname servidor_dhcp --net host alpine sh

Lo especial del comando anterior tiene que ver con el flag --net host, que provisiona al contenedor con el mismo stack de red que el host, tal como se menciona al inicio.

Si no tenemos errores con el comando anterior, el prompt del contenedor se mostrará en la consola, donde podemos empezar a trabajar. Lo primero que vamos a hacer es instalar DHCP con el comando apropiado, que para Alpine es:

apk add --no-cache dhcp

Luego de que la instalación finalice entonces debemos pasar a la configuración. Existen dos archivos importantes en la configuración del servicio DHCP. Uno es dhcpd.conf y el otro es dhcpd.leases. En el primero debemos definir cómo queremos asignar direccionamiento y el segundo es una base de datos que guarda las rentas que se asignan por medio de dhcp. Recuerden que la renta es un elemento principal del funcionamiento de dhcp.

Creación del archivo dhcpd.conf

Un archivo de muestra/ejemplo existe en la ruta /etc/dhcp. Pueden imprimir el contenido del archivo para visualizar información relacionada con las opciones que tenemos disponibles para configurar dhcp. Pueden utilizar una versión sencilla que yo he utilizado para esta demostración. Pueden descargar el archivo dentro del contenedor de la siguiente manera:

  1. Instalar curl con el comando apk add curl
  2. Descargar una copia del archivo de configuración con el comando curl https://raw.githubusercontent.com/hcastellon/Redes2-2021/main/dhcpd.conf -o dhcpd1.conf, donde le pasamos la URL donde se encuentra el archivo y usamos el flag -o para especificar el nombre del archivo de salida en la ruta actual. Si no se encuentra en /etc/dhcp, tendrá que copiar el archivo a esta ruta.
Figura 4. Descarga del archivo de configuración dhcp.

Para verificar el procedimiento anterior, liste los archivos y directorios e imprima el contenido en consola. Debe realizar los cambios necesarios de acuerdo con el entorno que tenga. De lo contrario, no funcionará y resultará en errores.

Pueden consultar mayor información acerca del archivo dhcpd.conf en la URL https://linux.die.net/man/5/dhcpd.conf

Creación de dhcpd.leases

El siguiente archivo que debemos crear antes de iniciar el servicio DHCP es dhcpd.leases. Es sencillo crearlo, por lo que solo debemos hacerlo tal como se crea un archivo cualquiera con el comando touch.

Iniciar el servicio

Si estuviéramos en un servidor con un sistema operativo completo, entonces utilizaríamos systemctl o init.d, por ejemplo. Pero en contenedores una forma de hacerlo es llamando directamente al programa de aplicación en la ruta /usr/sbin. En nuestro caso lo hacemos de la siguiente manera:

# /usr/sbin/dhcpd -4 -f -d -cf "/etc/dhcp/dhcpd.conf"

Los argumentos del comando significan lo siguiente:

  • -4: Iniciar el servicio para IPv4
  • -f: Iniciar el servicio en primer plano (foreground)
  • -d: Permite al servicio DHCP poder escribir al descriptor de archivo designado para errores (stderr)
  • -cf: Indica el archivo de configuración dhcpd que se debe cargar para el servicio.

Una vez ejecutado el comando, el servicio debería iniciar. Podemos ver el resultado directamente o, en una sesión aparte, ejecutar el comando docker logs nombre_contenedor y debería mostrar algo parecido a la siguiente figura:

Figura 5. Log de salida generado por el inicio del servicio DHCP.

Si no le pasamos el parámetro -d al servicio veríamos nada al ejecutar el comando docker logs.

Verificación del servicio

Habiendo completado los pasos anteriores, solo nos queda verificar que el servidor dhcp puede asignar direcciones IP a los clientes que lo soliciten. Podemos hacer que un cliente solicite direccionamiento en dos formas:

  1. Una vez que el sistema operativo ha arrancado, configurar la interfaz de red en modo dhcp para que encuentre servidores disponibles.
  2. De forma manual, por medio de la librería cliente de acuerdo con el tipo de sistema operativo. Por ejemplo, en Linux tenemos dhclient a disposición mientras que en Windows podemos hacerlo con ipconfig /renew para iniciar el proceso de solicitud.

Si lo hacemos con el primer método y estamos utilizando Alpine versión estándar, entonces cargamos la imagen en el CD/DVD de la máquina virtual y la arrancamos. Una vez haya cargado el prompt, podemos loguearnos escribiendo root como usuario de login. El prompt cambiará y para configurar entonces ejecutamos la utilidad setup-alpine, que nos lleva por un asistente de configuración. El punto importante es cuando llegamos a la configuración de las interfaces, en caso de que tengamos una (eth0) entonces vamos a elegir que sea dhcp. Aquí, el cliente debería contactar a un servidor DHCP por medio de los mensajes dhcp definidos en el protocolo y tomará la configuración ofrecida por el servidor. Del lado del servidor también podemos ver la conversación establecida tal como suele suceder en la arquitectura cliente-servidor. Esa conversación la podemos ver observar del lado del servidor tal como se muestra en la figura a continuación:

Figura 6. Asignación de renta del servidor DHCP al host ‘cliente’.

Pensamientos finales

Los contenedores suelen ser útiles en escenarios ciertamente complejos en los que necesitamos implementar roles o servicios de una manera ágil y que los recursos de cómputo no son suficientes. Este escenario sencillo puede ampliarse para provisionar diferentes subredes y configurar DHCP relay o configurar múltiples servidores DHCP para que funcionen en los modos de tolerancia a fallos: redundancia de servicios o balanceo de carga. Lo cierto es que hay mucho que se puede hacer y espero que este post haya sido útil.