SSD Nodes Learn Hosting plans →
Guías Matt ConnorPor Matt Connor

SSH mediante Tor sin abrir puertos entrantes

Configura sshd detrás de un servicio onion de Tor para cerrar todos los puertos entrantes. Incluye autorización v3 y el orden seguro para evitar perder el acceso al VPS.

Qué cambia al usar SSH mediante un servicio onion de Tor

SSH mediante un servicio onion de Tor permite administrar un VPS que no acepta conexiones entrantes en ningún puerto. El servidor se conecta a la red Tor y mantiene abierta esa conexión. La sesión SSH vuelve por ella, por lo que ningún proceso tiene que escuchar en la dirección IP pública.

El efecto en el registro es inmediato. Un servidor con un puerto SSH público recibe miles de intentos fallidos de contraseña al día procedentes de escáneres. Si coloca sshd detrás de un servicio onion y bloquea el tráfico entrante en el firewall, /var/log/auth.log sólo registra las sesiones que usted inició.

El coste es que tor interviene en todas las sesiones de administración. Es un daemon en el espacio de usuario que debe iniciarse y completar el arranque después de cada reinicio antes de que pueda iniciar sesión. Planifíquelo antes de cerrar el puerto, porque el fallo en este caso consiste en perder el acceso a una máquina a la que no puede llegar físicamente.

Obtenga una vía de acceso antes de cambiar nada

No empiece hasta disponer de una vía de recuperación que no use SSH.

Abra ahora la consola de su proveedor, ya sea la consola VNC o la consola serie del panel de control, e inicie sesión mediante ella. Si desconoce la contraseña de root, restablezca primero la contraseña de root desde el panel y confirme que funciona. Una consola que nunca se ha probado no es una vía de recuperación.

El orden siguiente es importante. Cada paso se verifica antes de ejecutar el siguiente, y el puerto 22 permanece abierto hasta que la ruta onion funciona.

  1. Instale tor y confirme que completa el arranque.
  2. Defina el servicio onion y consulte la dirección.
  3. Conéctese mediante onion mientras el puerto 22 todavía está abierto.
  4. Añada la autorización del cliente y vuelva a conectarse.
  5. Enlace sshd a loopback y cierre el puerto 22.
  6. Reinicie el servidor y vuelva a conectarse mediante onion.

Mantenga abierta su sesión SSH actual durante todo el proceso. Una sesión establecida sobrevive a un cambio del firewall que impediría abrir otra, por lo que es su primera línea de recuperación.

Instalar tor en el servidor

Ubuntu incluye tor en su propio repositorio, pero esa compilación suele estar desactualizada. El repositorio de Tor Project contiene la versión que describe su documentación. Añádalo con los comandos de su guía del repositorio apt.

sudo apt update
sudo apt install -y apt-transport-https wget gpg
wget -qO- https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc | gpg --dearmor | sudo tee /usr/share/keyrings/deb.torproject.org-keyring.gpg >/dev/null

Escriba /etc/apt/sources.list.d/tor.sources. Suites toma el nombre en clave de su versión, que muestra lsb_release -cs (noble en Ubuntu 24.04).

Types: deb deb-src
URIs: https://deb.torproject.org/torproject.org/
Suites: noble
Components: main
Signed-By: /usr/share/keyrings/deb.torproject.org-keyring.gpg
sudo apt update
sudo apt install -y tor deb.torproject.org-keyring
sudo journalctl -u tor@default -n 20 --no-pager

El registro debe terminar con Bootstrapped 100% (done). Si permanece detenido antes de ese punto, tor no puede acceder a la red. Casi siempre se debe a una regla del firewall de salida o a que el reloj tiene una hora incorrecta.

El nombre de la unidad puede inducir a error. systemctl status tor muestra Active: active (exited) incluso cuando todo funciona correctamente, porque Debian y Ubuntu empaquetan tor como una unidad maestra para varias instancias cuya única función es iniciar la instancia real. El daemon se ejecuta como tor@default.service. Use ese nombre para status y journalctl. Las operaciones de inicio, detención y recarga de tor también llegan a la instancia, por lo que sudo systemctl reload tor funciona como espera.

Defina el servicio onion para el puerto 22

Añada dos líneas a /etc/tor/torrc.

HiddenServiceDir /var/lib/tor/ssh/
HiddenServicePort 22 127.0.0.1:22

La segunda línea indica a tor que acepte el puerto virtual 22 en la dirección onion y se conecte a 127.0.0.1:22 en el equipo. Tor llega a sshd mediante loopback. Por eso sshd puede dejar de escuchar posteriormente en la dirección pública. Apunte esa segunda línea a un servidor web en 127.0.0.1:80 y las mismas dos directivas publican un sitio en una dirección onion, lo que resulta útil como segundo servicio una vez instalado tor.

sudo systemctl reload tor
sudo cat /var/lib/tor/ssh/hostname

Esto muestra 56 caracteres base32 seguidos de .onion. Esos caracteres son la clave pública del servicio en formato codificado. No intervienen ninguna autoridad de certificación ni ningún registro de nombres.

Deje que tor cree /var/lib/tor/ssh/. Si lo crea manualmente con un propietario incorrecto o con permisos más amplios que 0700, tor se niega a usarlo y el journal informa de que el directorio tiene permisos demasiado permisivos. Los archivos que contiene son la identidad del servicio: hs_ed25519_secret_key es la dirección. Haga una copia de seguridad de ese directorio con permisos 600 y conserve la copia fuera del equipo, porque perderlo implica obtener una dirección nueva y editar la configuración en cada cliente.

Conéctese desde su estación de trabajo

Su estación de trabajo necesita un cliente de tor que no requiere ninguna configuración. En Debian o Ubuntu, es sudo apt install -y tor netcat-openbsd. Después, Tor escucha en 127.0.0.1:9050 como proxy SOCKS5. SOCKS es un protocolo de proxy genérico. La versión 5 puede transportar un nombre de host en lugar de una dirección IP. Esa es la característica relevante en este caso.

OpenSSH no incluye su propio cliente SOCKS, por lo que un programa auxiliar establece la conexión. Añada esto a ~/.ssh/config.

Host myvps
  HostName xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.onion
  User admin
  ProxyCommand /usr/bin/nc -X 5 -x 127.0.0.1:9050 %h %p
  ServerAliveInterval 30

-X 5 selecciona SOCKS5 y -x 127.0.0.1:9050 apunta al tor local. %h entrega el nombre onion a tor como un nombre, para que tor lo resuelva dentro de la red. Debe usar OpenBSD netcat. GNU netcat no tiene la opción -X y termina con nc: invalid option -- 'X'.

ssh myvps

La primera conexión es lenta porque tor crea un circuito antes de realizar cualquier otra operación. Acepte la huella de la clave del host como lo haría en cualquier otra conexión. A partir de aquí, la gestión habitual de claves SSH se aplica sin cambios. El transporte ha cambiado. La autenticación no.

Para un uso puntual, puede omitir la entrada de configuración: torsocks ssh admin@xxxxx.onion hace el mismo trabajo.

Añadir autorización de cliente v3

Tal como está, cualquiera que conozca la dirección puede llegar al banner de SSH y empezar a probar contraseñas. Las direcciones onion no se pueden enumerar mediante el sistema de directorio, por lo que la dirección funciona como un secreto, pero puede filtrarse de formas habituales: en el historial del shell o en archivos de configuración confirmados en un repositorio de git. La autorización de cliente cierra esa brecha. El servicio publica su descriptor cifrado para una clave de cliente, por lo que alguien que tenga la dirección pero no la clave ni siquiera puede localizar el servicio.

Genere un par de claves x25519 en el cliente. Esta es la canalización de la guía de autorización de cliente del Tor Project, con un cambio.

openssl genpkey -algorithm x25519 -out /tmp/k1.prv.pem
grep -v " PRIVATE KEY" /tmp/k1.prv.pem | base64 -d | tail --bytes=32 | base32 | sed 's/=//g' > /tmp/k1.prv.key
openssl pkey -in /tmp/k1.prv.pem -pubout | grep -v " PUBLIC KEY" | base64 -d | tail --bytes=32 | base32 | sed 's/=//g' > /tmp/k1.pub.key

La versión publicada de esas líneas usa base64pem -d, que una instalación estándar de Ubuntu no incluye. El comando se detiene entonces con base64pem: command not found. GNU base64 -d decodifica el mismo cuerpo PEM, así que úselo en su lugar.

En el servidor, instale la clave pública.

sudo install -d -m 700 -o debian-tor -g debian-tor /var/lib/tor/ssh/authorized_clients
echo "descriptor:x25519:PASTE_PUBLIC_KEY_HERE" | sudo tee /var/lib/tor/ssh/authorized_clients/laptop.auth >/dev/null
sudo chown debian-tor:debian-tor /var/lib/tor/ssh/authorized_clients/laptop.auth
sudo systemctl reload tor

Sólo se leen los archivos que terminan en .auth. Guárdela como laptop.auth.txt y tor omitirá el archivo. Sólo dejará una advertencia en sus registros indicando que el archivo «must end in .auth». Si no revisa el journal, no lo notará y el servicio seguirá abierto para cualquiera que tenga la dirección.

En el cliente, instale la clave privada. En Ubuntu, el daemon de tor se ejecuta como el usuario debian-tor y no puede leer archivos de su directorio personal, así que mantenga el directorio en una ubicación a la que ese usuario pueda acceder.

sudo install -d -m 700 -o debian-tor -g debian-tor /var/lib/tor/onion_auth
echo "ADDRESS_WITHOUT_DOT_ONION:descriptor:x25519:PASTE_PRIVATE_KEY_HERE" | sudo tee /var/lib/tor/onion_auth/myvps.auth_private >/dev/null
sudo chown debian-tor:debian-tor /var/lib/tor/onion_auth/myvps.auth_private
sudo chmod 600 /var/lib/tor/onion_auth/myvps.auth_private

Añada ClientOnionAuthDir /var/lib/tor/onion_auth al /etc/tor/torrc del cliente y recargue tor. Si ejecuta tor con su propio usuario, por ejemplo con la compilación de Homebrew en macOS, apunte ClientOnionAuthDir a ~/.tor/onion_auth con el modo 0700.

La dirección dentro de ese archivo tiene 56 caracteres sin el sufijo .onion. Elimine /tmp/k1.prv.pem y /tmp/k1.prv.key cuando termine.

Ahora pruebe ambas direcciones. ssh myvps debería seguir conectándose. Desde una máquina que no tenga ninguna clave, la misma dirección debería fallar. Ese fallo demuestra que la autorización está activa.

Cierre el puerto 22 en este orden

Establezca primero una red de seguridad. Este único comando revierte los dos cambios siguientes después de quince minutos si pierde el acceso.

sudo systemd-run --on-active=15m --unit=ssh-rescue \
  /bin/sh -c 'ufw allow 22/tcp; rm -f /etc/systemd/system/ssh.socket.d/override.conf; systemctl daemon-reload; systemctl restart ssh.socket'

Cancélelo con sudo systemctl stop ssh-rescue.timer una vez que confirme que la ruta onion sigue funcionando.

A continuación, haga que sshd deje de escuchar en la dirección pública. Ubuntu 24.04 activa ssh mediante una unidad de socket de forma predeterminada, por lo que ssh.socket controla el socket de escucha, no sshd. ListenAddress en sshd_config se sigue aplicando: el paquete openssh de Ubuntu incluye un generador de systemd que lee sshd_config y escribe las direcciones de escucha del socket a partir de ese archivo. Esto sólo ocurre cuando systemd vuelve a ejecutar sus generadores, por lo que un cambio en ListenAddress se aplica después de sudo systemctl daemon-reload y sudo systemctl restart ssh.socket, no después de reiniciar sshd. Los pasos siguientes configuran directamente el socket, de modo que el temporizador de rescate anterior puede revertir el cambio eliminando un archivo. Compruebe en qué caso se encuentra.

systemctl is-enabled ssh.socket

Si muestra enabled, ejecute sudo systemctl edit ssh.socket y añada esto.

[Socket]
ListenStream=
ListenStream=127.0.0.1:22

El ListenStream= vacío borra el valor heredado de la unidad proporcionada por el paquete. Si omite esa línea, añadirá un segundo listener y conservará el público. Esta es la forma más habitual de que este paso falle sin indicarlo.

sudo systemctl daemon-reload
sudo systemctl restart ssh.socket
ss -tlnp | grep ':22'

ss debería mostrar 127.0.0.1:22 y nada en 0.0.0.0:22. Si ssh.socket estaba deshabilitado, escriba ListenAddress 127.0.0.1 en /etc/ssh/sshd_config.d/10-onion.conf, ejecute sudo systemctl restart ssh y vuelva a comprobarlo con la misma línea ss. Esa salida es la prueba en ambos casos.

Después, configure el firewall, que es una gestión normal de reglas de ufw en un VPS. Ejecute primero sudo ufw status numbered y elimine la regla SSH que muestre.

sudo ufw status numbered
sudo ufw delete allow OpenSSH
sudo ufw default allow outgoing
sudo ufw default deny incoming
sudo ufw status verbose

Deje permitido el tráfico saliente. Tor se conecta a relays en puertos como 443 y 9001, por lo que una política saliente predeterminada de denegación impide que tor complete el arranque y elimina al mismo tiempo la única forma de acceso que queda. La mayoría de los proveedores también ejecutan un firewall de red independiente en el panel de control. Cierre allí el puerto 22, o el puerto seguirá siendo accesible independientemente de lo que indique ufw.

Si Docker se ejecuta en este equipo, compruebe sus puertos publicados antes de dar por terminado el trabajo. Docker escribe sus propias reglas en las mismas tablas y publica los puertos de los contenedores directamente, sin pasar por ufw, por lo que una política de denegación de ufw no muestra toda la situación.

Reinicie antes de confiar en él

systemctl is-enabled tor@default
sudo reboot

Si el primer comando no indica que el servicio está habilitado, ejecute sudo systemctl enable tor@default antes de reiniciar. Espere dos minutos y, después, ejecute ssh myvps. Tor debe iniciar el proceso de arranque después del inicio del sistema, por lo que la dirección onion empieza a responder un tiempo después de que la máquina esté disponible.

Si nunca vuelve a estar disponible, abra la consola y lea sudo journalctl -u tor@default -b. Allí se muestra cualquier error de sintaxis de torrc o problema de permisos del directorio. También puede comprobar una modificación de torrc antes de aplicarla.

sudo -u debian-tor tor --verify-config

Lo que cuesta frente a un túnel de WireGuard

En comparación con una VPN de WireGuard en su propio VPS, un servicio onion es más lento y menos predecible. Evalúe con honestidad esta diferencia antes de decidirse.

Latencia. El circuito del cliente tiene tres relays y el lado del servicio añade otros tres, por lo que las pulsaciones atraviesan aproximadamente seis máquinas elegidas al azar en todo el mundo. La escritura interactiva tiene un retraso visible y las copias de archivos son lentas. WireGuard añade un salto. Mida su caso concreto con time ssh myvps 'echo ok', porque el valor depende del circuito que tor haya creado y cambia cuando tor crea otro.

Un daemon en espacio de usuario en la ruta crítica. WireGuard está en el kernel y se inicia junto con la red. Tor es un proceso que debe iniciarse, completar el arranque y alcanzar un relay guard antes de que algo funcione. Si falla, tendrá que usar la consola del proveedor.

Precisión del reloj. Los descriptores de servicios onion se publican según periodos de tiempo, por lo que un reloj muy desfasado impide resolver la dirección sin mostrar un mensaje claro en ningún sitio. timedatectl debe informar System clock synchronized: yes.

A cambio, obtiene una exposición que ya no depende de que una regla del firewall sea correcta. No hay ningún puerto que escanear ni ningún banner que capturar. Además, la dirección es una clave pública, por lo que el endpoint demuestra su identidad antes de que SSH se inicie.

En la práctica, normalmente conviene usar ambos. Ejecute WireGuard como ruta diaria y conserve el servicio onion como ruta alternativa para cuando la configuración de WireGuard sea incorrecta. Así sólo deja abierto un puerto UDP, en lugar de un puerto SSH público. Nada de esto sustituye a proteger el propio sshd: la autenticación exclusiva mediante claves y el acceso sin root siguen siendo importantes, porque un servicio onion protege la ruta de red y nada más.

Modos de fallo y errores que verá

Tor nunca supera Bootstrapped 0%. El tráfico saliente está bloqueado o el reloj está muy desfasado. Compruebe la política de salida con sudo ufw status verbose y después ejecute timedatectl.

systemctl status tor muestra active (exited). Esto es normal en Debian y Ubuntu. Consulte tor@default en su lugar.

No se encuentra el descriptor. Tor devuelve el error extendido SOCKS F0, "Onion Service Descriptor Can Not be Found". El descriptor aún no se ha publicado, lo que puede tardar un poco después de una recarga, o tor no se está ejecutando en el servidor.

F4, "Onion Service Missing Client Authorization". El cliente no tiene ningún .auth_private coincidente que tor pueda usar. Compruebe que ClientOnionAuthDir esté en torrc, que el directorio tenga el modo 0700, que el nombre de archivo termine en .auth_private y que debian-tor pueda leerlo. Una clave con formato incorrecto también produce este error: un = final o un salto de línea no válido dentro de la cadena base32 hace que tor rechace el archivo al cargarlo, y el registro del cliente indica que la longitud de la clave privada base32 no es válida o que la clave no se puede descodificar.

F5, "Onion Service Wrong Client Authorization". Tor cargó una clave privada con el formato correcto, pero no coincide con el archivo .auth del servidor, por lo que el descriptor no se puede descifrar. Las causas habituales son una clave de otro par o un archivo .auth del servidor que se sustituyó después de configurar el cliente.

nc: invalid option -- 'X'. Está instalado GNU netcat en lugar de la versión de OpenBSD. Ejecute sudo apt install -y netcat-openbsd.

Could not resolve hostname. ssh intentó usar DNS normal, que no tiene una respuesta para .onion, por lo que ProxyCommand nunca se ejecutó. El patrón Host de ~/.ssh/config no coincide con el nombre que escribió.

Permission denied (publickey). El túnel funcionó y tor ya terminó. Trátelo como un problema normal de permisos publickey denegado y no atribuya la causa a tor.

FAQ

¿Un servicio onion implica realmente que no haya puertos abiertos en mi VPS?

Sí, una vez que sshd está enlazado a 127.0.0.1 y el firewall descarta el tráfico entrante. Tor establece una conexión TCP saliente con un relay y la sesión vuelve a través de él, por lo que ningún proceso del equipo acepta conexiones en la dirección pública. Compruébelo con ss -tlnp en el servidor y con un escaneo de puertos desde otro lugar. No olvide el firewall de red del proveedor en el panel de control. Es independiente de ufw y también debe estar cerrado.

¿La dirección .onion proporciona suficiente seguridad por sí sola para SSH?

No. La dirección tiene 56 caracteres y no se puede adivinar ni enumerar mediante el sistema de directorio, por lo que se comporta como un secreto, pero puede quedar expuesta en el historial del shell y en los archivos de configuración. Añada autorización de cliente v3. Con ella, el descriptor del servicio se cifra para la clave de su cliente, de modo que alguien que sólo tenga la dirección recibe el error extendido F4 y nunca llega a sshd.

¿Qué ocurre si tor no se inicia después de reiniciar?

Pierde por completo el acceso SSH, porque la dirección onion es entonces la única vía de entrada. Por eso debe probar la consola del proveedor antes de cerrar el puerto 22. Tor también necesita tiempo para iniciar después del arranque, por lo que la dirección responde más tarde que la máquina al ping. Si nunca responde, inicie sesión en la consola y lea sudo journalctl -u tor@default -b. Allí se muestra un posible error de sintaxis de torrc o un problema de permisos en /var/lib/tor/ssh.

¿SSH sobre Tor es más lento que WireGuard?

Sí, por un margen amplio. Una conexión a un servicio onion atraviesa unos seis relays elegidos al azar, mientras que WireGuard utiliza un único salto cifrado directamente hasta el servidor. La escritura en el terminal presenta retraso y las transferencias son lentas. Una configuración habitual es usar WireGuard para el trabajo diario y conservar el servicio onion como ruta de emergencia que siga disponible si se rompe la configuración de la VPN.