Métodos de conexión remota a instancias EC2 Linux en AWS: comparativa y buenas prácticas
¿Prefieres leer? El texto completo del artículo, más abajo, es la transcripción de esta narración (disponible solo en inglés).
La administración remota se ha convertido en un pilar del éxito de las infraestructuras cloud. En un mundo donde la movilidad y la flexibilidad son esenciales, poder gestionar servidores y recursos desde cualquier ubicación es ya indispensable para empresas y profesionales de IT.
Si buscas mejorar la seguridad y la flexibilidad de tus conexiones remotas, en este artículo repaso todos los métodos posibles y te doy el conocimiento necesario para tomar decisiones informadas, eligiendo el método más adecuado según tus necesidades. Exploro cómo algunos de estos métodos se complementan entre sí, lo que me permite reforzar progresivamente la seguridad, reducir la carga operativa, y simplificar la administración de mi infraestructura.
Conexión mediante pares de claves SSH de larga duración
Para conectarme remotamente a un servidor desde mi ordenador, puedo usar Secure Shell (SSH). SSH es un protocolo de red que ofrece una conexión segura y cifrada para ejecutar comandos en un servidor. Su seguridad se basa en el uso de criptografía asimétrica, que implica un par de claves. En este sistema, el usuario en su ordenador tiene la clave privada, mientras que el servidor remoto tiene la clave pública. Este par de claves se usa para verificar la legitimidad del usuario que intenta establecer la conexión con el servidor.
Antes de desplegar mi instancia de Amazon Elastic Compute Cloud (EC2), es vital generar un par de claves en la región de AWS donde voy a desplegarla. Hecho esto, descargo la clave privada del par de claves a mi ordenador. Esta clave privada se usará para conectarme al servidor. Durante el proceso de despliegue de la instancia EC2, hará falta indicar el uso del par de claves recién creado, lo que permite desplegar la instancia con la clave pública correspondiente. Este tipo de pares de claves SSH se conocen como pares de claves de larga duración, ya que tienen una vida útil extendida y la misma clave se reutiliza para múltiples conexiones a lo largo del tiempo.
Para poder establecer una conexión SSH con una instancia EC2, es imprescindible asegurarse de que la instancia sea accesible desde Internet. Para ello, la instancia EC2 debe desplegarse en una subred pública con una tabla de rutas correctamente configurada. Esta tabla de rutas debe permitir que todo el tráfico destinado a 0.0.0.0/0 se enrute hacia una puerta de enlace a Internet asociada a la Amazon Virtual Private Cloud (VPC). Con estos pasos, la instancia tendrá una dirección IP pública y será accesible desde cualquier punto de Internet.
Para habilitar las conexiones SSH en mi instancia, tengo que asegurarme de que esté asociada a un Security Group (SG) adecuado, ya que las conexiones SSH se establecen mediante tráfico TCP (Transmission Control Protocol) en el puerto 22. Para ello, añado una regla de entrada al security group, permitiendo tráfico TCP en el puerto 22 con origen 0.0.0.0/0, es decir, desde cualquier punto de Internet. Sin embargo, si quiero restringir las conexiones SSH solo a IPs concretas y conocidas, puedo configurar el security group para aceptar tráfico SSH únicamente desde esas IPs. Esta medida mejora la seguridad de mis instancias, ya que cualquier intento de conexión SSH desde IPs no autorizadas será rechazado por el security group. Así tengo un control más preciso sobre quién puede acceder a mi instancia, reduciendo riesgos potenciales.
Una vez completados estos pasos, ya puedo ejecutar el siguiente comando desde mi ordenador, sustituyendo los parámetros correspondientes, para establecer la conexión:
chmod 400 [PATH-TO-PRIVATE-KEY]
ssh -i [PATH-TO-PRIVATE-KEY] [USER-NAME]@[SERVER-ADDRESS]
La seguridad de este escenario de claves de larga duración depende de la correcta generación y protección de las claves SSH. Es vital que la clave privada se mantenga almacenada de forma segura en el ordenador de cada usuario. Además, es imprescindible establecer políticas y procedimientos de gobernanza para la gestión segura de las claves, incluyendo su rotación periódica y su revocación en caso de compromiso o pérdida.
Al hacer conexiones SSH independientes para cada uno de mis servidores, no tengo un sistema unificado para auditar las conexiones SSH hechas por los usuarios. Esto obliga a acceder a cada uno de los servidores individualmente para revisar los logs que genera cada uno. La falta de centralización dificulta detectar y analizar actividades sospechosas, haciendo el proceso de auditoría más complejo y laborioso.
Si quieres probar este escenario en tu cuenta de AWS y conectarte a una instancia EC2 usando el método descrito en el diagrama, he creado esta plantilla de CloudFormation para que despliegues los recursos necesarios para probarlo: LongLived-SSHKeyPair-InstanceWithPublicIP.yaml
Conexión mediante pares de claves SSH efímeras con EC2 Instance Connect
Cuando establezco conexiones SSH a mis instancias de la forma tradicional, usar pares de claves puede resultar complejo y tedioso, sobre todo al generar, distribuir y almacenar de forma segura las claves privadas. Además, gestionar múltiples claves para distintas instancias puede convertirse en una tarea engorrosa y propensa a errores. EC2 Instance Connect elimina la necesidad de gestionar pares de claves, ya que este servicio usa un par de claves nuevo cada vez que se establece una conexión; este tipo de claves se conoce como pares de claves efímeras.
EC2 Instance Connect se puede usar desde la consola de administración de AWS, lo que me da una experiencia de consola SSH desde el navegador, o mediante la AWS Command Line Interface (CLI). Al establecer la conexión con cualquiera de estos métodos, EC2 Instance Connect hace una llamada a la API (Application Programming Interface) de AWS y envía la clave pública SSH a la instancia, que se almacena en los metadatos de la instancia durante 60 segundos.
Para poder enviar la clave pública, el usuario debe estar asociado a una política de Identity Access Management (IAM) que lo permita. A diferencia de una conexión SSH tradicional, donde los usuarios acceden a las instancias compartiendo la clave privada, con EC2 Instance Connect simplemente concedo permisos mediante IAM. Esto me da mayor control sobre qué usuarios pueden acceder a qué instancias, y me permite revocar esos permisos en cualquier momento.
Para establecer la conexión, el usuario debe tener habilitadas las acciones «ec2-instance-connect:SendSSHPublicKey» y «ec2:DescribeInstances» en su política de IAM. Puedo personalizar las políticas para dar acceso al usuario a instancias concretas o incluso a instancias con una etiqueta particular, lo que da flexibilidad a la gestión de permisos de acceso.
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "ec2-instance-connect:SendSSHPublicKey",
"Resource": [
"arn:aws:ec2:[REGION]:[ACCOUNT-ID]:instance/[INSTANCE-ID] "
],
"Condition": {
"StringEquals": {
"ec2:osuser": "[AMI-USER-NAME]"
}
}
},
{
"Effect": "Allow",
"Action": "ec2:DescribeInstances",
"Resource": "*"
}
]
}
Al usar EC2 Instance Connect, todas las conexiones SSH quedan registradas en CloudTrail, el servicio de auditoría de AWS, lo que permite un seguimiento detallado y centralizado de quién, cuándo y desde dónde se ha accedido a las instancias.
Es importante señalar que EC2 Instance Connect solo admite conexiones usando IPv4 y no es compatible con IPv6. Además, esta funcionalidad no está disponible en todas las regiones de AWS. Para usar EC2 Instance Connect, hay que comprobar antes si está habilitada en la región que estoy usando.
Otro aspecto crucial es que, para usar EC2 Instance Connect, hace falta tener instalado el paquete correspondiente en la instancia. Por defecto, este paquete viene preinstalado en las AMIs de Amazon Linux 2 (versión 2.0.20190618 o posterior) y Ubuntu (versión 20.04 o posterior). Se puede instalar manualmente en AMIs de Amazon Linux 2 (cualquier versión) o Ubuntu (versión 16.04 o posterior).
Si quieres probar este escenario en tu cuenta de AWS y conectarte a una instancia EC2 usando el método descrito en el diagrama, he creado esta plantilla de CloudFormation para que despliegues los recursos necesarios para probarlo: Ephemeral-SSHKeyPair-InstanceWithPublicIP.yaml
EC2 Instance Connect desde la consola de administración de AWS
Si quiero establecer la conexión desde la consola de administración de AWS, es importante que la instancia esté desplegada en una subred pública, que permita el acceso desde Internet. Además, tengo que asociar un security group que permita tráfico entrante en el puerto 22 para habilitar las conexiones SSH.
EC2 Instance Connect, cuando se usa desde la consola de administración de AWS, usa rangos de direcciones IP específicos para las conexiones SSH. Por eso es posible restringir el security group para permitir tráfico solo desde esas IPs concretas. Con este método, el proceso de enviar la clave pública al servidor y usar la clave privada para establecer la conexión ocurre de forma transparente, sin tener que preocuparme por generar un par de claves efímeras.
EC2 Instance Connect mediante AWS CLI
Si quiero establecer la conexión mediante AWS CLI, hay dos escenarios posibles. Si quiero conectarme a mis instancias desde Internet, necesito desplegar la instancia en una subred pública y habilitar tráfico entrante en el puerto 22 desde cualquier IP, o restringirlo a un grupo fijo de IPs desde las que sé que voy a conectarme.
Si tengo conectividad privada a mi instancia (tengo una VPN o Direct Connect desplegados), no hace falta que mis instancias estén en subredes públicas. Pueden estar en subredes privadas y puedo conectarme a través de su dirección IP privada.
Lo primero que necesito hacer para iniciar una conexión SSH es generar un par de claves nuevo o usar uno que ya tenga generado en mi ordenador. Puedo generar un par de claves SSH con el siguiente comando.
ssh-keygen -t rsa -f [KEY-NAME]
Una vez que tengo mi par de claves efímeras, uso el siguiente comando de AWS CLI para enviar la clave pública a los metadatos de la instancia con la que quiero establecer la conexión.
aws ec2-instance-connect send-ssh-public-key \
--region [REGION] \
--availability-zone [AVAILABILITY-ZONE] \
--instance-id [INSTANCE-ID] \
--instance-os-user [USERNAME] \
--ssh-public-key file:// [KEY-NAME].pub
Durante ese periodo de 60 segundos, puedo iniciar la conexión SSH con la clave privada asociada a la clave pública que se ha enviado a la instancia. Para establecer la conexión SSH, puedo usar el siguiente comando.
ssh -o "IdentitiesOnly=yes" -i [KEY-NAME] [USER-NAME]@[SERVER-ADDRESS]
Conexión SSH a través de un Bastion Host
Si quiero mantener mis instancias en subredes privadas de la VPC y evitar exponer el puerto 22 a Internet, una solución eficaz es usar un Bastion Host. Este enfoque consiste en usar una instancia intermedia entre el origen y la instancia final. El Bastion Host actúa como punto de entrada expuesto a Internet, al que accedo para establecer la conexión. Una vez dentro, puedo iniciar una nueva conexión usando la dirección IP privada de la instancia final. Así consigo mayor seguridad al restringir el acceso directo desde Internet a mis instancias, a la vez que facilito la administración remota.
Para implementar esta solución, asocio una Internet Gateway a mi VPC. Después, creo una subred pública y configuro una tabla de rutas para que el tráfico destinado a 0.0.0.0/0 se dirija a la Internet Gateway, mientras que el tráfico destinado al rango de la VPC se enruta localmente. Dentro de la subred pública despliego mi instancia EC2, que asumirá el rol de Bastion Host. Para esta instancia defino un security group que permite tráfico entrante en el puerto 22, ya sea desde cualquier IP o restringido a IPs concretas si conozco su origen. Para restringir las conexiones que puede iniciar el Bastion Host, añado una regla de salida al security group que permite únicamente tráfico SSH saliente hacia el security group asociado a las instancias con las que quiero conectarme.
Ahora creo una subred privada con una tabla de rutas que solo enrutará tráfico dentro de la VPC. En esta subred despliego mis instancias EC2 finales a las que quiero acceder por SSH. Es importante asociar estas instancias a un security group que permita tráfico entrante en el puerto 22, pero solo desde el security group previamente asociado a mi instancia Bastion Host. Así me aseguro de que mi máquina virtual solo sea accesible por SSH a través del Bastion Host, y que no se puedan establecer conexiones directas desde Internet.
Para poder establecer la conexión con el Bastion Host, necesito tener un par de claves. A su vez, mi Bastion Host debe tener los pares de claves necesarios para conectarse después a cada uno de los servidores en las subredes privadas a los que quiero acceder.
Si estoy usando AMIs compatibles con EC2 Instance Connect, puedo combinar la solución del Bastion Host con EC2 Instance Connect. Al hacerlo, elimino la necesidad de gestionar claves privadas y públicas tanto para conectarme al Bastion Host como para conectarme desde el Bastion Host a las instancias finales.
Para acceder a mi Bastion Host mediante EC2 Instance Connect, tengo la opción de hacerlo tanto desde la consola de administración de AWS como usando AWS CLI. Sin embargo, para acceder a las instancias finales en subredes privadas desde el Bastion Host, solo puedo usar AWS CLI, y hará falta apuntar a las direcciones IP privadas de mis servidores.
Para permitir que el Bastion Host use AWS CLI y se conecte mediante EC2 Instance Connect a los servidores finales, es imprescindible asociarlo a un rol de IAM que incluya una política que permita establecer las conexiones. Así me aseguro de que el Bastion Host tenga los permisos adecuados para interactuar con las instancias en subredes privadas de forma segura y controlada. Además, tendré que añadir una nueva regla de salida al security group para permitir tráfico saliente por el puerto 443 hacia el destino 0.0.0.0/0. Esto es necesario para que, al ejecutar el comando de la CLI desde mi Bastion Host, pueda alcanzar el endpoint de la API de EC2 Instance Connect.
Si quieres probar estos dos escenarios en tu cuenta de AWS y conectarte a una instancia EC2 usando los métodos descritos en los diagramas, he creado estas plantillas de CloudFormation para que despliegues los recursos necesarios para probarlos: LongLived-SSHKeyPair-BastionHost.yaml y Ephemeral-SSHKeyPair-BastionHost.yaml
Conexión SSH sin necesidad de IP pública con AWS Client VPN
Usar una instancia EC2 como bastion host tiene la ventaja de no exponer directamente mis instancias finales a Internet. Sin embargo, esta solución también tiene desventajas importantes. Primero, supone costes adicionales por mantener la instancia en funcionamiento, y hay que asumir la responsabilidad de gestionarla y mantenerla, incluyendo aplicar parches de seguridad y realizar tareas administrativas recurrentes. Introducir una instancia adicional añade complejidad al entorno y puede complicar la configuración y la resolución de problemas. Es importante señalar que el Bastion Host se convierte en un punto único de fallo, y la única forma de abordarlo es incorporando Bastion Hosts adicionales en otras zonas de disponibilidad, lo que aumenta aún más la carga operativa.
AWS Client VPN es un servicio de VPN gestionado basado en OpenVPN que permite un acceso seguro a tus instancias EC2. Al implementar este método, se establece un túnel cifrado entre tu ordenador y la VPC, dando visibilidad a las instancias mediante una IP privada. Esto elimina la necesidad de exponer ningún servidor en una subred pública accesible desde Internet. Al ser un servicio gestionado por AWS, se elimina la carga operativa y administrativa presente en el escenario del Bastion Host.
AWS Client VPN ofrece tres métodos de autenticación para el usuario al conectarse al endpoint. Por sencillez, en este ejemplo uso autenticación mutua basada en certificados. Estos certificados son formas de identificación digital emitidas por una autoridad certificadora. El primer paso es crear un certificado y una clave para el servidor, y otro certificado y clave para el cliente.
Al crear un endpoint de Client VPN, indico la subred a la que quiero asociarlo. En esta subred (o subredes) se crearán Elastic Network Interfaces (ENIs) a las que se les asignará un security group. Para mayor control, añado una regla de salida al security group que solo permite tráfico saliente hacia el security group de las instancias finales con las que quiero conectarme. Además, asocio una tabla de rutas al endpoint, indicando que el tráfico destinado al CIDR (Classless Inter-Domain Routing) de la VPC se enruta localmente. También puedo usar las Authentication Rules disponibles para permitir el acceso a todo el rango de la VPC con una sola regla para todos los grupos.
Una vez desplegada y configurada mi VPN, tengo que descargar el fichero de configuración del endpoint de Client VPN y distribuirlo entre los usuarios para que puedan acceder mediante un cliente compatible con OpenVPN.
Con conectividad privada a la VPC a través de la VPN, puedo establecer una conexión SSH de forma tradicional usando el par de claves con el que desplegué la instancia EC2, conectándome a la dirección IP privada de la instancia. Si estoy usando AMIs compatibles con EC2 Instance Connect, puedo olvidarme de usar pares de claves SSH de larga duración y conectarme a mis instancias mediante AWS CLI usando pares de claves SSH efímeras, igual que en el escenario del Bastion Host.
Si quieres probar este escenario en tu cuenta de AWS y conectarte a una instancia EC2 usando el método descrito en el diagrama, he creado esta plantilla de CloudFormation para que despliegues los recursos necesarios para probarlo: VPN.yaml
Conexión SSH sin necesidad de IP pública con EC2 Instance Connect Endpoint
Desplegar una VPN desde mi ordenador hasta la VPC para administrar instancias de forma remota puede ser un enfoque excesivo. Aunque ya no tengo una instancia haciendo de Bastion Host, lo que me libraría de su gestión, AWS Client VPN, como servicio gestionado, sigue implicando cuestiones de configuración, administración y autenticación de usuarios, sea cual sea el método elegido de los tres posibles.
Recientemente, AWS ha lanzado una nueva funcionalidad llamada EC2 Instance Connect Endpoint (EIC Endpoint). Gracias a esta funcionalidad, ahora puedo establecer conexiones a mis instancias en subredes privadas sin necesidad de un Bastion Host intermediario, una IP pública, ni siquiera una Internet Gateway en mi VPC.
El EIC Endpoint es un proxy TCP que incorpora un sistema de autenticación, permitiendo autenticar la conexión mediante políticas de IAM. A continuación doy un ejemplo de cómo permitir que un usuario se conecte a un EIC Endpoint concreto, usando solo el puerto 22 y fijando un límite de tiempo para la conexión a una instancia en particular.
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "EC2InstanceConnect",
"Action": "ec2-instance-connect:OpenTunnel",
"Effect": "Allow",
"Resource": "arn:aws:ec2:[REGION]:[ACCOUNT-ID]:instance-connect-endpoint/[EIC-ENDPOINT-ID]",
"Condition": {
"NumericEquals": {
"ec2-instance-connect:remotePort": "22"
},
"IpAddress": {
"ec2-instance-connect:privateIpAddress": "[SUBNET-CIDR]"
},
"NumericLessThanEquals": {
"ec2-instance-connect:maxTunnelDuration": "[TIME]"
}
}
},
{
"Sid": "Describe",
"Action": [
"ec2:DescribeInstances",
"ec2:DescribeInstanceConnectEndpoints"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
Tengo dos métodos para establecer una conexión a las instancias finales. En el primero, uso AWS CLI para generar un túnel WebSocket seguro desde mi máquina hasta el endpoint, usando mis credenciales de IAM. Una vez establecido el túnel, simplemente hago SSH a la dirección local (127.0.0.1 o localhost) y me conecto como de costumbre. En la segunda opción, uso la consola de administración de AWS, que crea el túnel por mí de forma transparente e inicia una conexión SSH en una terminal desde el navegador.
Al desplegar un EIC Endpoint, tengo que indicar en qué subred (o subredes) quiero implementarlo. En estas subredes se creará una ENI nueva, a la que debo asociar un security group. Para reforzar la seguridad del entorno, añado una regla de salida que permite tráfico SSH hacia el security group asociado a las instancias finales con las que quiero conectarme.
Por el lado de mis instancias finales, hará falta añadir una regla de tráfico entrante en el puerto 22 desde el security group asociado a la ENI. Así me aseguro el acceso correcto a mis instancias a través del EIC Endpoint.
Al establecer conexiones mediante EIC Endpoint usando AWS CLI, tengo dos opciones. La primera consiste en abrir el túnel y después establecer una conexión SSH de la forma tradicional usando claves de larga duración. Esta opción garantiza compatibilidad con cualquier AMI de Linux que esté usando. Para ello, puedo usar el siguiente comando:
ssh ec2-user@[INSTANCE] \
-i [SSH-KEY] \
-o ProxyCommand='aws ec2-instance-connect open-tunnel \
--instance-id %h'
Por otro lado, si estoy usando AMIs compatibles con EC2 Instance Connect, puedo aprovechar la capacidad de conectarme por SSH usando claves efímeras. Para ello, puedo usar el siguiente comando:
aws ec2-instance-connect ssh --instance-id [INSTANCE]
Si quieres probar este escenario en tu cuenta de AWS y conectarte a una instancia EC2 usando el método descrito en el diagrama, he creado esta plantilla de CloudFormation para que despliegues los recursos necesarios para probarlo: InstanceConnectEndpoint.yaml
Conexión basada en agente con Systems Manager Session Manager
¿Y si te dijera que hay una forma mucho más sencilla de acceder a mis instancias remotas que no requiere exponer servidores a Internet, no requiere abrir el puerto 22, y no usa SSH, eliminando así la necesidad de preocuparse por las claves? Todo esto es posible gracias a Session Manager, de AWS Systems Manager (SSM).
SSM Session Manager funciona mediante un agente, compatible con Linux, Mac y Windows. No solo funciona con tus instancias EC2 en AWS, sino que también puedes instalarlo en máquinas virtuales que tengas en local. El agente se encarga de establecer la comunicación entre el servidor y el servicio Session Manager. Esta comunicación fluye a través de túneles bidireccionales sobre una conexión HTTPS.
Para iniciar una sesión remota, puedes usar la consola de administración de AWS o la CLI. Cuando pides una sesión, SSM genera un token de sesión y establece una conexión segura. Para que el agente instalado en la instancia pueda comunicarse con la API de SSM, hace falta asignarle permisos mediante un rol y permitir conectividad HTTPS a las siguientes direcciones:
- ec2messages.[REGION].amazonaws.com
- ssm.[REGION].amazonaws.com
- ssmmessages.[REGION].amazonaws.com
Si tus instancias están en una subred pública con una Internet Gateway asociada a la VPC y una regla de salida en el puerto 443 en el security group, podrán acceder a estas direcciones. Si prefieres mantener las instancias en subredes privadas, tienes dos opciones: implementar una NAT Gateway que les dé acceso a Internet, o configurar un VPC Endpoint en la VPC, dándoles conectividad privada a la API de SSM.
Todas las sesiones iniciadas mediante Session Manager quedan registradas en CloudTrail. Esto significa que se mantiene un registro detallado de quién accede a qué instancias, cuándo se establecen las conexiones, qué comandos se ejecutan, y cuándo se cierran las sesiones. Estos logs de CloudTrail dan una visión completa de la actividad, simplificando la detección de posibles vulnerabilidades y garantizando el cumplimiento normativo.
Si quieres iniciar conexiones usando AWS CLI, es imprescindible tener instalado en tu máquina local el plugin de Session Manager para AWS CLI. Una vez instalado, puedes ejecutar el siguiente comando:
aws ssm start-session --target [INSTANCE-ID]
Si quieres probar este escenario en tu cuenta de AWS y conectarte a una instancia EC2 usando el método descrito en el diagrama, he creado esta plantilla de CloudFormation para que despliegues los recursos necesarios para probarlo: SSM-SessionManager.yaml
¡Gracias por leer! Nos vemos en el próximo.