Try   HackMD

SSH como herramienta para el acceso a máquinas creadas en Google Compute Engine

Introducción a SSH

SSH o Secure Socket Shell permite ejecutar una shell en un servidor remoto, de forma que dicha shell se pueda manipular a través de una red, utilizando para ello un canal TCP. Además, la interacción con la shell debe de hacerse de forma segura. Como los canales TCP no proporcionan una comunicación segura, es necesario añadir una capa de seguridad adicional que cifre los datos. Estas son las funcionalidades más relevantes de SSH. En resumen, mediante un cliente SSH podremos acceder a las máquinas sobre las que vayamos a operar de forma segura y eficiente.

En los siguientes apartados se introducirán algunos aspectos importantes relacionados con SSH y su relación con el acceso a máquinas virtuales creadas en Google Cloud Engine.

Encriptación y cifrado

La encriptación se basa en el cifrado con una clave de un mensaje cuyo contenido se quiere proteger. SSH utiliza tanto el cifrado simétrico como el cifrado asimétrico.

Cifrado simétrico

En el cifrado simétrico se utiliza la misma clave tanto para el cifrado como el descifrado. Esto significa que la clave debe ser secreta (conocida solo por el emisor y el receptor). El mayor problema de este cifrado es cómo puede hacer llegar el emisor al receptor la clave de forma segura.

Cifrado asimétrico

En el cifrado asimétrico se utilizan dos claves relacionadas entre sí: una privada (secreta) y otra pública. Las claves tienen la propiedad de que un mensaje cifrado por una determinada clave pública solo puede ser desencriptado utilizando la correspondiente clave privada (y si es encriptado con la clave privada, solo puede desencriptarse con la pública).

Esto permite que el emisor pueda utilizar la clave pública del receptor para encriptar el mensaje y solo éste pueda desencriptarlo. Por otro lado, si el propietario de la clave privada la utiliza para cifrar un mensaje, el destinatario (si la puede desencriptar con la clave pública del emisor) está identificando y autenticando al remitente.

Aunque el cifrado asimétrico soluciona el intercambio de claves entre los participantes en la comunicación, tiene varios problemas en comparación con el cifrado simétrico, como un mayor tamaño de los mensajes codificados y necesidad de un mayor tiempo de proceso.

Protocolo SSH

El protocolo SSH determina la secuencia de eventos para lograr una comunicación segura entre dos host. Resumidamente esta consiste en:

  • Handshake encriptado (usando cifrado asimétrico) para que el cliente pueda verificar que se está comunicando con el servidor.
  • La capa de transporte de conexión entre el cliente y la máquina remota se encripta mediante un código simétrico que se establece algorítmicamente sin necesidad de ser explícitamente intercambiado, lo cual aumenta la seguridad.
  • El cliente se autentica ante el servidor.
  • El cliente interactúa con la máquina remota sobre la conexión encriptada.

El servidor se identifica ante el cliente con una llave (o clave) de host única. La primera vez que se conecta el cliente la llave no existirá y será necesario que la valide el cliente (este es un paso crítico y debería asegurarse, quizás por otros medios, que la llave es correcta). Esta llave se guarda en el cliente y en sucesivas ocasiones vale para comprobar la identidad del servidor. En ciertos casos (por ejemplo, cambios en la máquina servidora) puede variar esta llave y es necesario eliminar la guardada para aceptar la nueva.

Orden ssh

La orden ssh es un cliente SSH disponible en sistemas UNIX/Linux (hay múltiples clientes de SSH, por ejemplo PuTTY para Windows). La máquina que actué como servidora debe estar ejecutando sshd (SSH Daemon) y debe de poder escuchar las conexiones TCP/IP en el puerto 22, que es el que utiliza por defecto SSH.

Su sintaxis básica es:

	ssh user@hostname

donde hostname es la máquina remota a la cual se desea conectar y user es el usuario de la máquina remota con el que se conectará (debe, por tanto, existir en dicha máquina).

La orden ssh
tiene múltiples opciones y utiliza dos ficheros de configuración. Los datos de configuración se obtienen por este orden de precedencia:

  1. Opciones en la línea de comando del ssh
  2. Fichero de configuración de ssh del usuario (~/.ssh/config)
  3. Fichero de configuración de ssh del sistema (/etc/ssh/ssh_config)

El archivo de configuración de usuario se describe más adelante en este documento.

Generación del archivo de claves

Para generar un par clave pública/privada se puede hacer:

$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/ogarcia/.ssh/id_rsa):

Se pueden dar un nombre concreto a la clave, incluso tener más de una, pero la clave que se usa siempre por defecto es id_rsa.

Esta orden genera dos archivos, uno con la clave pública (.pub) y otro con la clave privada. Es importante recordar que debemos hacer una copia de seguridad de la clave privada, ya que si la perdemos (o simplemente nos cambiamos de máquina) no podremos acceder a los recursos (las máquinas virtuales) a los que dicha clave da acceso.

$ ls -l ~/.ssh
-rw-------@ 1 ogarcia  staff  1799 Feb 15  2019 /Users/ogarcia/.ssh/id_rsa
-rw-------@ 1 ogarcia  staff   401 Apr 21  2016 /Users/ogarcia/.ssh/id_rsa.pub

También es importante usar el modo 400 en ambos archivos, ya que configuraciones de seguridad más relajadas hacen que ssh se niegue a usar dichas claves por inseguras.

Uso de las claves en Compute Engine.

Las claves se pueden usar de dos formas: de forma global para todas las máquinas que pertenezcan a un proyecto, o de forma individual para una máquina concreta.

Para usar la clave de forma global debemos acceder al proyecto de Google Cloud -> Compute Engine -> Metadatos

En la barra de pestañas, arriba a la izquierda, accedemos a la opción Claves ssh. Podemos añadir la clave que generamos con ssh-keygen pulsando el botón de Añadir Clave.

La clave que vamos a añadir es, por supuesto, la clave pública. Como recomendación, y ante la duda de qué clave utilizar en cada ocasión, si la pública o la privada, siempre hay que tener en cuenta que la clave privada nunca debe salir de nuestro ordenador (y mucho menos para ponerla en Google Cloud), debemos mantenerla segura y en secreto. Por su parte, la pública puede difundirse como su nombre indica, públicamente.

La clave pública tiene el siguiente formato:

ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAvfGpt7F7eCFfJJnSHOAY1TVGHw/T2WAHHw3sPK2/KUR90N4n6liqpm29ScXLTFaJX6m9dswblMej1LjEQO5FYBx7QhVJoDjascCs9ikxHGlRPER8lgq/DJGywR51ihpEM2CjQ6xk61/9tsl/89Bh4FwdhqydS0nvHxLZ3jhMLiDyFH/7kNfNufsrgd3neQfqdvRJnl8W7MPahVWrcqWHQMFQ19yX2A5uDWqUmZVxxsvaDzocDb5On3tV7+kxmIjiKEMV5jhz7e0fJfQfASFKF13rAQVCZNMrW/er7R6KlTxqnCF3BHu3OCOoyHu+9NB3AfPhon/PRObFLZP/4X7iuw
== o.garcia@everywhere

Simplificando mucho su formato (más detalles en RFC 4716 - The Secure Shell (SSH) Public Key File Format), la primera parte es la cadena literal ssh-rsa, seguida de la clave en sí, y por último un campo de comentario, en el que generalmente se pone un email con objeto de identificar la clave. No obstante, para GCE, esta dirección de email tiene un significado especial: el nombre de usuario de dicha dirección (el nombre a la izquierda de la arroba) lo emplea como nombre de usuario en las máquinas de GCE. Por lo tanto, a la hora de pegarlos en la consola tenemos que segurarnos de que dicha dirección tiene el campo de usuario que nosotros queramos.

Configuración de SSH

Para hacer más cómodo el uso de ssh con muchas máquinas implicadas podemos utilizar un archivo de configuración que debe colocarse en ~/.ssh/config. Es muy posible que este archivo no exista y deba crearse desde cero.

Host operaciones
    Hostname 35.234.81.207
    User ogarcia
    ForwardAgent yes
    IdentityFile ~/.ssh/id_rsa

En el ejemplo anterior, operaciones es un identificador arbitrario, fácil de recorar para nosotros, ya que lo utilizaremos para hacer, por ejemplo: ssh operaciones, y por lo tanto da nombre a una máquina concreta. La directiva Hostname sirve para indicar cual es la IP o el nombre DNS de la máquina, User el nombre del usuario con el que nos conectaremos (y que tiene que coincidir con el especificado en la clave pública subida a google cloud) y por último IdentityFile indica dónde se encuentra la clave privada que hay que usar para conectar con la máquina. La directiva ForwardAgent yes permite activar una funcionalidad denominada Agent Forwarding de la que hablaremos más adelante.

Agent Forwarding

Hay un caso de uso de las conexiones ssh que merece atención. Supongamos que podemos acceder desde mi máquina local

L1 a una máquina
M1
. Desde esta máquina es posible a su vez acceder a otra máquina
M2
, pero esta última
M2
no es accesible desde
L1
, solo a través de
M1
. Este escenario es habitual cuando tenemos un conjunto de máquinas, pero no queremos exponer todas ellas a internet (generalmente por motivos de seguridad). En lugar de esto, lo que hacemos es exponer una única máquina,
M1
en el ejemplo, acceder a esta y desde ella saltar a las otras máquinas que necesitemos. El problema es que para acceder a
M2
desde
M1
necesitariamos tener en esta última máquina la clave privada, pero nuestra clave privada está en
L1
y no debe salir de ahí. La solución es activar un proceso en nuestras máquinas denominado ssh-agent. Este agente recuerda nuestra clave privada y acepta peticiones de cifrado de otros agentes. Cuando el agente de
M2
pregunte por la clave privada y vea que no dispone de ella, preguntará al agente de la máquina desde la que procede la conexión, es decir,
M1
, y esta a su vez a
L1
.

Transferencia de información entre máquinas utilizando scp y rsync

Para transferir información de forma sencilla se puede utilizar scp, que funciona como su equivalente cp pero permite especificar que el origen y/o el destino sean máquinas remotas accesibles mediante SSH. En el caso que ambas máquinas, origen y destino, sean remotas es importante tener en cuenta que la transferencia de datos se hace directamente entre ellas, es decir, que el origen deber tener visible el destino y tener acceso a él.

El formato para especificar el origen o destino, cuando es local es el mismo que si usáramos cp. En el caso remoto se especifica de la siguiente forma: usuario@host:path. El usuario es un usuario con acceso a la máquina host, y con permisos para leer el archivo indicado en el path.

La orden scp soporta la expansión del * pero si queremos copiar asimismo directorios es necesario poner el modificador -r (recursive).

Otra forma más eficiente, en la mayoría de los casos, para transferir información es utilizando rsync. La operativa básica es idéntica a la de scp, pero rsync comprueba si el archivo que vamos a transferir existe ya en el destino. En caso negativo simplemente lo copia como haría scp, pero en caso afirmativo comprueba primero si ambos, origen y destino, difieren, y evita transferir datos si son iguales. Se puede decir que rsync mantiene origen y destino sincronizados, de ahí su nombre. Los flags de invocación más interesantes son:

  • -a, --archive, indica modo de archivado, que equivale a -rlptgoD, lo que significa que debe copiar directorios si los hubiera, transferir archivos que representen manejadores de dispositivo, respetar enlaces simbólicos, fechas de modificación, información de pertenecia a usuarios y grupos y permisos de acceso.
  • -z, --compress, indica que se debe comprimir la información en tránsito
  • -v, --verbose, indica que se debe informar del progreso de la sincronización (ver también -P --partial-progress)
  • --delete, indica que se deben borrar archivos en el destino que hayan sido eliminados en el origen.
  • -e en el caso de que se quiera utilizar otro tipo de shell que no sea ssh

Ejercicios

  • Hacer una copia de seguridad remota de los contenidos de directorio home del usuario en la máquina de google cloud

Túneles SSH

Los túneles SSH permiten desviar el tráfico de puertos TCP, tanto locales como remotos, a través del socket que da servicio al propio ssh de forma segura. Dicho de otra forma, el canal de comunicaciones que se establece entre la máquina cliente y la máquina servidor se puede utilizar para enviar más datos que los propios que se mandan y se reciben de la shell, por ejemplo los dirigidos a un determinado puerto TCP. Dependiendo si este puerto es el de la máquina cliente o de la máquina servidora se habla de túneles locales o remotos respectivamente.

Túneles locales

Permite dirigir el tráfico a un puerto TCP de la máquina cliente a cualquier puerto de cualquier máquina accesible por la máquina servidora. La sintaxis para indicar este tipo de túneles es la siguiente:

ssh -L PUERTO_LOCAL:MAQUINA_REMOTA:PUERTO_REMOTO

Por ejempo, desde nuestra máquina podemos hacer una petición GET a ip.jsontest.com para obtener nuestra dirección IP haciendo:

local$ curl https://api.my-ip.io/ip.json	
{"success": true, "ip": "34.142.120.59", "type": "IPv4"}

Ahora vamos a hacer que la llamada la haga nuestra máquina de operaciones en google. Para ello abrimos un túnel local a nuestro puesto 8787 (arbitrario).

ssh -L 8787:api.my-ip.io:443 operaciones

Esta orden abrirá una conexión con la máquina operaciones como si fuera una conexión ssh habitual, y de hecho lo es. Manteniendo esa sesión abierta, en otra ventana de terminal, en nuestra máquina local hacemos un intento de conexión al puerto 8787 de la siguente forma:

curl --insecure https://localhost:8787/ip.json 
{"ip": "35.228.71.152"}
tags: ASO-GISI ASO-GIT 2022-2023