Desde hace tiempo quería dejar de depender de mi laptop como único lugar para programar. La idea era simple: tener una máquina Debian encendida en casa, correr ahí mis proyectos de PHP, TypeScript, Python y Go, y poder conectarme por SSH desde mi MacBook o mi iPad para seguir desarrollando desde donde sea, sin importar en qué red esté.
En este post documento paso a paso cómo lo armé: instalación de SSH, firewall, acceso remoto seguro con Tailscale, llaves SSH desde Mac e iPad con Termius, y los entornos de lenguaje que instalé.
Lo que vamos a construir
- Un Debian con SSH endurecido (solo acceso por llave, sin contraseñas)
- Firewall (ufw) que bloquea todo excepto lo necesario
- Tailscale como VPN privada para entrar desde cualquier red sin abrir puertos en el router
- Acceso desde macOS (Terminal / VS Code Remote-SSH) y iPad (Termius)
- Entornos listos para PHP, Node/TypeScript, Python y Go
1. Instalar y activar SSH
En Debian 13 (trixie), el servidor SSH no viene instalado por defecto:
sudo apt update
sudo apt install -y openssh-server
sudo systemctl enable --now ssh
systemctl status ssh
Con esto el servicio queda activo y arrancando automáticamente en cada reinicio.
2. Firewall con ufw
Antes de exponer nada, cierro todo lo que no necesito:
sudo apt install -y ufw
sudo ufw allow OpenSSH
sudo ufw --force enable
sudo ufw status verbose
Regla por defecto: deniega todo el tráfico entrante, permite todo el saliente, y solo abre el puerto 22 (SSH).
3. Tailscale: acceso remoto sin abrir puertos
En vez de hacer port-forwarding en el router (exponiendo SSH directo a internet, con todo el riesgo que eso implica), usé Tailscale: crea una red privada tipo VPN entre tus dispositivos usando WireGuard, sin configurar nada en el router.
Instalación en el Debian:
curl -fsSL https://tailscale.com/install.sh | sh
sudo systemctl enable --now tailscaled
sudo tailscale up --ssh --hostname=debian-dev
El comando tailscale up imprime una URL de autenticación:
To authenticate, visit:
https://login.tailscale.com/a/XXXXXXXXXXXXX
La abrí en el navegador, entré con mi cuenta, y listo. Verifico que quedó conectado:
tailscale status
tailscale ip -4
Esto me da una IP privada fija tipo 100.x.y.z que solo es alcanzable desde mis propios dispositivos conectados a la misma tailnet — sin importar si estoy en la misma red WiFi o en el LTE del iPad.
Nota: usé la flag
--ssh, que además habilita el SSH nativo de Tailscale (autenticación basada en tu identidad de Tailscale, sin necesidad de llaves). Es un extra útil, pero de todas formas configuré SSH tradicional con llaves para que funcione con cualquier cliente estándar como Termius.
4. Llaves SSH desde macOS
En la Mac, generé una llave dedicada:
ssh-keygen -t ed25519 -C "mac-jesus" -f ~/.ssh/id_ed25519
Le puse passphrase (si alguien roba la laptop, la llave privada no sirve de nada sin ella).
Copié la llave pública:
cat ~/.ssh/id_ed25519.pub
Y la agregué al Debian:
# en el Debian
mkdir -p ~/.ssh && chmod 700 ~/.ssh
echo "ssh-ed25519 AAAA... mac-jesus" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
Para no escribir la IP completa cada vez, en la Mac dejé un alias en ~/.ssh/config:
Host debian-dev
HostName 100.x.y.z
User avelar
IdentityFile ~/.ssh/id_ed25519
Con eso, conectarse es solo:
ssh debian-dev
Para desarrollo real desde la Mac uso VS Code con la extensión “Remote – SSH”: seleccionas el host debian-dev y editas/corres el código directo en el Debian, con terminal integrada, como si fuera local.
5. Llaves SSH desde iPad con Termius
En el iPad instalé Termius (disponible en App Store):
- Pestaña de llaves (ícono de llave) → + → Generate Key
- Tipo ED25519, la nombré
ipad-jesus - Copié la llave pública desde la app y la agregué al mismo
~/.ssh/authorized_keysdel Debian - Creé un nuevo Host en Termius con:
- Address: la IP de Tailscale del Debian (
100.x.y.z) - Username: mi usuario del sistema
- Key: la llave
ipad-jesus
Con Tailscale instalado también en el iPad (App Store, misma cuenta), Termius puede alcanzar el Debian esté donde esté — casa, calle, con datos móviles.
6. Cerrar la puerta: solo acceso por llave
Una vez confirmé que ambas llaves (Mac e iPad) funcionaban, desactivé el login por contraseña en /etc/ssh/sshd_config.d/:
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
sudo systemctl restart ssh
Desde este punto, sin la llave privada correcta, nadie entra — ni siquiera con la contraseña del sistema.
7. Entornos de desarrollo
Con el acceso resuelto, instalé los lenguajes que uso día a día.
PHP + Composer
sudo apt install -y php-cli php-fpm php-mbstring php-xml php-curl php-mysql php-sqlite3 php-zip unzip
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
Node.js / TypeScript (vía nvm)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
nvm install --lts
nvm alias default 'lts/*'
npm install -g typescript pnpm
Python
sudo apt install -y python3 python3-pip python3-venv pipx
pipx ensurepath
Go
Descargué el tarball oficial desde go.dev/dl y lo extraje en /usr/local/go, agregando export PATH=$PATH:/usr/local/go/bin al .bashrc.
Resultado
Hoy tengo el Debian corriendo 24/7 en casa, con:
- SSH accesible únicamente por llave, desde cualquier red gracias a Tailscale
- Firewall cerrado por defecto
- PHP 8.4, Node 24 LTS + TypeScript, Python 3.13 y Go listos para usar
- Acceso fluido desde la Mac (VS Code Remote-SSH) y desde el iPad (Termius), incluso fuera de casa
El siguiente paso natural es dejar corriendo tmux o screen en el servidor para que los procesos de desarrollo sobrevivan aunque cierre la sesión SSH — pero eso lo dejo para otro post.
¿Tienes dudas sobre algún paso? Déjalo en los comentarios.
Leave a Reply