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.

Gaining access back into a Microsoft Azure VM

As an IT pro I’ve been using mostly Azure infrastructure-as-a-service services for a while. Using the cloud poses a new set of challenges in the way we the admins are used to manage our infrastructure resources, that is, we install the OS server in the hardware we want and have full access anytime we want. The point is that, in the Microsoft Azure cloud, things change a bit (depending on the perspective) because you don’t have access to the physical resources. Yeah, I know, you don’t have to worry about where and how to deploy your servers and you can spin up a VM in a matter of seconds. After that short intro I want to address a specific scenario you can come across when using IaaS services.

So here it is the scenario: A student [came to me and] has been assigned to deploy a DNS/DHCP server using a VM in Microsoft Azure and he already knows that a server must be configured with a static IP address, thing is he wasn’t aware manual IP configuration in Azure isn’t the same as in your on-premises datacenter servers/equipment or personal computer. So he changed the IP address inside the VM assigning one from a different address space and right after that he couldn’t get in again anymore. He’s got locked out.

So how you can assign a static IP address for an Azure VM?

You can assign a static IP address in two different ways. The first and most recommended is trough the Azure portal. Just click the virtual machine in the virtual machines tab (in the left panel) and click the network interface card, a new tile will open. Go to the IP configurations on the left panel and look for the column Private IP address as you can see in the following image

Dynamic IP address

As you can see private IP addresses are assigned dynamically so to change this just click the IP address and one (more) tile will open where you can toggle the assignment to static

Static

Just remember to assign an IP address in the same subnet and you know is available.

The second method is less convenient. You can change the IP address inside the virtual machine to assign a static one you know it’s in the address space defined for your subnet. The tricky thing with this method and what makes it different from the portal is that if the IP address you set is already in use the VM won’t warn you about it taking the new configuration but then you won’t be able to connect to your VM again so as we call it you’ve got locked out of your own VM, it’s like closing your house door with the keys inside. What happens next is the nice thing about the cloud.

Hold on there, Azure’s got your back!

In the past I’ve seen people forgetting about the credentials they had set for their VMs and Azure has provided always a way to reset passwords so they can gain access back to their resources. But this is different, it’s not about credentials but about network connectivity. As a result, Azure has taken steps further and provide a neat functionality called “Run Command”.

RUn command

Click a VM resource and drill down till you get to the Operations part and there you will find the Run command. This section provides a set of commands you can run to a VM and see the outputs to those commands all from the portal.

run options

What about if none of the commands suit your needs? Well, you have the magic one: RunPowershellScript. This functionality leverages the VM agent allowing you to run commands independently of the network or remote management configuration you have for a virtual machine.

Getting back to the issue presented above, using the “Run Powershell Command” was the right one. Since an invalid IP address was assigned to the VM we needed to revert that change to a valid one. The command issued for this was a well-known one:

Get-NetIPAddress # Grab the Interface index number and IP address you want to change
Remove-NetIPAddress -InterfaceIndex ifindexnumber -IPAddress a.b.c.d #a.b.c.d is the invalid IP Address

If you don’t get any errors at this point you can set the new (and valid) IP address with the following command:

New-NetIPAddress -InterfaceIndex ifindexnumber -IPAddress a.b.c.d -PrefixLength 24 -verbose

As you can see it’s kind of easy to resolve the issue but handy at the same time to know you can use these ‘backdoor’ tools in case something terrible like the one I describe happens. If you spot any errors don’t hesitate to leave a comment and I’ll correct them.

Hope you can find this useful and informative!