SSH tunnels
Persistent SSH tunnel between two VPS with autossh: a receiver service shows up on the initiator's localhost without being exposed.
- Category: Servers and infrastructure
- Status: Available
- Requirements: Two VPS (from the same company for the automatic flow); Being a company member with access to both VPS; Systems with systemd and a compatible OpenSSH (check with
ver_funciones_vps, feature “SSH tunnels between VPS”) - MCP tools (Read only):
ver_estado_receptor_tunel_vps,listar_claves_receptor_tunel_vps,ver_estado_iniciador_tunel_vps,listar_tuneles_vps,ver_tunel_vps - MCP tools (Write or modify):
configurar_receptor_tunel_vps,agregar_clave_receptor_tunel_vps,configurar_iniciador_tunel_vps,crear_tunel_vps,controlar_tunel_vps - MCP tools (Destructive):
configurar_tunel_entre_vps,eliminar_receptor_tunel_vps,eliminar_clave_receptor_tunel_vps,eliminar_iniciador_tunel_vps,eliminar_tunel_vps
What can you do with it?
- Use a service listening on one VPS (a database, an internal API) from another VPS as if it were on
localhost, without publishing it on the internet. - Set everything up in a single call (
configurar_tunel_entre_vps), or step by step. - Authorize or revoke the keys allowed into the receiver.
- Start, stop or restart each tunnel, and see its live status.
When should you use it?
- An app on one VPS needs the database living on another.
- Splitting an internal API from its frontend without opening public ports for the service.
How does it work in ArduMaker?
It is a local forward (ssh -L) running on the initiator: whatever listens on the receiver's puerto_remoto becomes available at localhost:puerto_local inside the initiator. It does not work the other way round and exposes nothing to the internet.
The steps
- Receiver (
configurar_receptor_tunel_vps): creates a user dedicated to the tunnel, configuressshdto accept that user on port 2222 (no terminal, no X11, no agent, TCP forwarding only) and publishes 2222 through a port forward. It runs in the background. - Initiator (
configurar_iniciador_tunel_vps): installsautosshand generates an ed25519 key pair. Also in the background. - Key: the initiator's public key is authorized on the receiver (
agregar_clave_receptor_tunel_vps). - Tunnel (
crear_tunel_vps): creates on the initiator a systemd serviceardutunnel_<nombre>runningautossh -L <local>:<target>:<port>against the receiver, enables it and starts it.
configurar_tunel_entre_vps does all four steps and also starts the tunnel. It waits for each role to finish installing (up to a few minutes); if one does not finish in time, it stops with tunel_rol_instalando and can be resumed with desde. A role already installed is not reinstalled, a key already authorized is reused and a tunnel already created with that name and local port comes back as it is.
What actually changes?
On the receiver
- A tunnel user with its authorized keys in its own
authorized_keysblock. - An ArduMaker-managed
sshdconfiguration file that adds port 2222 with itsMatch LocalPortblock (port 22 stays declared). Do not edit it by hand: it is fully rewritten whenever a role changes. - A public port forward to the VPS port 2222.
On the initiator
- The
autosshpackage and an ed25519 key pair for the tunnel user. - One
ardutunnel_<nombre>.serviceper tunnel, withRestart=always, enabled at boot. - A tunnel-specific
known_hosts: the receiver's first key is pinned and, if it later changes, the tunnel does not connect.
Ports and connection
| Item | Value |
|---|---|
| Receiver port inside the VPS | 2222 |
| Public port | The host_port of the forward to 2222 (public_port) |
| Host | The receiver dnsip |
| Connection string | <usuario>@<dnsip> -p <public_port> |
ExecStart=/usr/bin/autossh -M 0 -N \
-o "ServerAliveInterval=30" -o "ServerAliveCountMax=3" \
-o "StrictHostKeyChecking=accept-new" -o "ExitOnForwardFailure=yes" \
-L <puerto_local>:localhost:<puerto_remoto> \
-i <clave_del_tunel> -p <public_port> <usuario>@<dnsip>ver_estado_receptor_tunel_vps returns the connection string and a usage example to connect from another machine with ssh -N -L.
Diagram
- App on the initiator: localhost:<puerto_local>
- autossh: ardutunnel_<nombre> service
- Receiver port forward: <dnsip>:<public_port> → 2222
- Receiver sshd :2222: tunnel user only, forwarding only
- Receiver service: localhost:<puerto_remoto>
Expected result
ver_tunel_vpsshows the tunnel with the live status of its systemd service.listar_tuneles_vpslists the initiator's tunnels;ver_estado_iniciador_tunel_vpsreturns its public key and the tunnel count.- From the initiator, the service answers at
localhost:<puerto_local>.
systemctl status ardutunnel_mi-base
nc -vz localhost 15432Errors and troubleshooting
| Answer | Cause | What to do |
|---|---|---|
409 tunel_rol_instalando | A role is still installing | Ask again; it resumes from there |
tunel_rol_fallido | A role installation failed | Check the role status and retry |
tunel_mismo_vps | Receiver and initiator are the same VPS | Use two different VPS |
| 409 local port in use | Another initiator tunnel already uses that local_port | Pick another port |
409 Key already registered | The key is already authorized | Nothing to do |
| The tunnel does not connect | The receiver key changed, or the initiator key is not authorized | Check ver_logs_systemd_vps for the ardutunnel_<nombre> service |
If a step of the flow fails, whatever can be undone is undone; installed packages remain and are reported in residuo (reinstalling changes nothing). The flow accepts idempotency_key and simular.
MCP access
Yes, in the /mcp and /mcp/vps profiles. Installing a role returns an operacion_id: ver_operacion says when it is ready.
| Tool | Type |
|---|---|
configurar_tunel_entre_vps | Write (full flow) |
ver_estado_receptor_tunel_vps, listar_claves_receptor_tunel_vps, ver_estado_iniciador_tunel_vps, listar_tuneles_vps, ver_tunel_vps | Read |
configurar_receptor_tunel_vps, configurar_iniciador_tunel_vps, agregar_clave_receptor_tunel_vps, crear_tunel_vps | Write |
controlar_tunel_vps | Write (stop drops traffic) |
eliminar_tunel_vps, eliminar_clave_receptor_tunel_vps, eliminar_receptor_tunel_vps, eliminar_iniciador_tunel_vps | Destructive |
Reference at /mcp/docs and examples at /mcp/tuneles.