# Conexión básica ssh usuario@ip-del-vps # Especificar puerto (si no es el 22) ssh -p 2222 usuario@ip-del-vps # Especificar clave privada ssh -i ~/.ssh/mi_clave usuario@ip-del-vps # Conexión con clave y puerto personalizado ssh -i ~/.ssh/mi_clave -p 2222 usuario@ip-del-vps # Ejecutar un comando remoto sin abrir sesión ssh usuario@ip-del-vps "df -h" ssh usuario@ip-del-vps "systemctl status nginx" # Copiar archivos con SCP scp archivo.txt usuario@ip:/ruta/destino/ scp -r carpeta/ usuario@ip:/ruta/destino/ # Sincronizar con rsync (más eficiente que scp) rsync -avz carpeta/ usuario@ip:/ruta/destino/
Definí alias para no tipear la IP, usuario y clave cada vez. Archivo en ~/.ssh/config (en tu máquina local).
# ~/.ssh/config # Alias para tu VPS principal Host donweb HostName 123.456.789.0 User mi_usuario Port 2222 IdentityFile ~/.ssh/id_ed25519 # Otro servidor Host staging HostName 98.76.54.32 User deploy IdentityFile ~/.ssh/id_ed25519_staging # Opciones globales (aplican a todos) Host * ServerAliveInterval 60 # keepalive cada 60s ServerAliveCountMax 3 AddKeysToAgent yes
# Con esto alcanza con escribir: ssh donweb scp archivo.txt donweb:/var/www/
Una de las mejoras de productividad más grandes para trabajar con SSH diariamente.
# Ver info del servidor conectado whoami # usuario actual hostname # nombre del host uptime # tiempo encendido y carga uname -a # kernel y arquitectura # Ver conexiones SSH activas who w last | head -20 # historial de logins # Desconectarse exit logout # o Ctrl+D # Escape para comandos especiales dentro de SSH # ~. → desconectar (si la sesión cuelga) # ~? → ver todos los escapes disponibles
Si la sesión SSH se cuelga y no responde, escribí Enter ~ . (intro, tilde, punto) para forzar el cierre sin cerrar la terminal.
Se generan dos archivos: la clave privada (queda en tu máquina, nunca la compartas) y la clave pública (la que se copia al servidor).
# Ed25519 — algoritmo moderno, recomendado ssh-keygen -t ed25519 -C "mi-email@ejemplo.com" # RSA 4096 bits — mayor compatibilidad ssh-keygen -t rsa -b 4096 -C "mi-email@ejemplo.com" # Con nombre de archivo personalizado ssh-keygen -t ed25519 -f ~/.ssh/id_donweb -C "donweb-vps"
Te va a pedir una passphrase. Usala — agrega una capa extra de protección si alguien roba el archivo de clave privada.
# Método automático (recomendado) ssh-copy-id usuario@ip-del-vps # Especificando clave y puerto ssh-copy-id -i ~/.ssh/id_ed25519.pub -p 2222 usuario@ip-del-vps # Método manual (si ssh-copy-id no está disponible) cat ~/.ssh/id_ed25519.pub | ssh usuario@ip-del-vps \ "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys" # En el servidor: verificar que quedó bien cat ~/.ssh/authorized_keys
Probá que la conexión con clave funciona ANTES de deshabilitar la autenticación por contraseña.
SSH es muy estricto con los permisos de archivos. Si están mal, el servidor rechaza la autenticación por clave sin dar demasiadas explicaciones.
# En el servidor, ejecutar como el usuario objetivo chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys # El directorio home tampoco debe ser world-writable chmod 755 ~ # Verificar permisos actuales ls -la ~/.ssh/ stat ~/.ssh/authorized_keys
Si la autenticación por clave falla sin motivo aparente, los permisos son lo primero a revisar.
El agente SSH guarda la clave desbloqueada en memoria para no pedir la passphrase en cada conexión.
# Iniciar el agente eval "$(ssh-agent -s)" # Agregar clave al agente ssh-add ~/.ssh/id_ed25519 # Agregar con timeout (8 horas) ssh-add -t 8h ~/.ssh/id_ed25519 # Ver claves cargadas en el agente ssh-add -l # Eliminar todas las claves del agente ssh-add -D
En macOS podés agregar UseKeychain yes en ~/.ssh/config para que el Keychain maneje todo automáticamente.
# Ver todas las claves autorizadas cat ~/.ssh/authorized_keys # Agregar una clave manualmente echo "ssh-ed25519 AAAA... comentario" >> ~/.ssh/authorized_keys # Revocar una clave (editar el archivo y borrar la línea) nano ~/.ssh/authorized_keys # Ver fingerprint de una clave pública ssh-keygen -lf ~/.ssh/id_ed25519.pub # Ver fingerprint de las claves del servidor ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub ssh-keygen -lf /etc/ssh/ssh_host_rsa_key.pub
Archivo de configuración del servidor SSH en /etc/ssh/sshd_config. Cada cambio requiere reiniciar el servicio.
# /etc/ssh/sshd_config — configuración recomendada ## Puerto ## # Cambiar del 22 dificulta ataques automatizados Port 2222 ## Protocolo y algoritmos ## Protocol 2 HostKey /etc/ssh/ssh_host_ed25519_key HostKey /etc/ssh/ssh_host_rsa_key ## Autenticación ## PermitRootLogin no # nunca root directo PasswordAuthentication no # solo claves SSH PubkeyAuthentication yes AuthorizedKeysFile .ssh/authorized_keys PermitEmptyPasswords no ChallengeResponseAuthentication no ## Restricciones de acceso ## MaxAuthTries 3 # máx intentos por conexión MaxSessions 5 LoginGraceTime 30 # seg para autenticarse ## Usuarios permitidos (solo los que necesitan) ## AllowUsers mi_usuario deploy ## Comportamiento de sesión ## ClientAliveInterval 300 # keepalive cada 5 min ClientAliveCountMax 2 X11Forwarding no AllowTcpForwarding no # deshabilitar si no usás túneles PrintMotd no Banner none # no revelar info del servidor ## Logging ## SyslogFacility AUTH LogLevel VERBOSE
# Verificar configuración antes de aplicar sshd -t # Aplicar cambios systemctl restart sshd
Si cambiás el puerto o deshabilitar contraseñas, abrí una segunda sesión SSH ANTES de cerrar la actual para asegurarte de que podés reconectarte.
Este es el proceso correcto para no quedarse afuera del servidor.
# PASO 1: Desde tu máquina local, generar y copiar la clave ssh-keygen -t ed25519 -f ~/.ssh/id_donweb ssh-copy-id -i ~/.ssh/id_donweb.pub usuario@ip-del-vps # PASO 2: Probar que la autenticación con clave funciona # (abrir una NUEVA terminal, no cerrar la actual) ssh -i ~/.ssh/id_donweb usuario@ip-del-vps # PASO 3: Recién ahora, deshabilitar contraseñas en sshd_config nano /etc/ssh/sshd_config # → PasswordAuthentication no # PASO 4: Verificar y recargar sshd -t && systemctl restart sshd # PASO 5: Verificar desde otra terminal que sigue funcionando ssh -i ~/.ssh/id_donweb usuario@ip-del-vps
# Ver estado systemctl status sshd # Ver logs de autenticación en tiempo real journalctl -u sshd -f # Ver intentos de login fallidos journalctl -u sshd | grep "Failed" grep "Failed password" /var/log/auth.log | tail -20 # Ver logins exitosos grep "Accepted" /var/log/auth.log | tail -20 # IPs que más intentos fallidos tienen grep "Failed password" /var/log/auth.log \ | awk '{print $11}' | sort | uniq -c | sort -rn | head -10
Fail2ban monitorea los logs del sistema y banea automáticamente las IPs que hacen demasiados intentos fallidos. Funciona con SSH, Nginx, y cualquier servicio que genere logs.
apt install fail2ban systemctl enable fail2ban systemctl start fail2ban
Fail2ban agrega reglas a iptables/nftables automáticamente para bloquear las IPs. No requiere configurar el firewall manualmente para que funcione.
Nunca editás jail.conf directamente — creás un jail.local que lo sobreescribe. Archivo en /etc/fail2ban/jail.local
# /etc/fail2ban/jail.local [DEFAULT] # Tu IP para nunca banearte a vos mismo ignoreip = 127.0.0.1/8 ::1 TU_IP_FIJA_AQUI bantime = 3600 # baneado por 1 hora (en segundos) findtime = 600 # ventana de tiempo para contar intentos maxretry = 5 # intentos antes de banear backend = systemd # leer logs de journald # Protección SSH [sshd] enabled = true port = 2222 # tu puerto SSH personalizado filter = sshd logpath = /var/log/auth.log maxretry = 3 # más estricto que el default bantime = 86400 # baneado 24 horas # Protección Nginx — demasiadas peticiones 4xx [nginx-http-auth] enabled = true port = http,https logpath = /var/log/nginx/error.log # Protección contra escaneos de bots [nginx-botsearch] enabled = true port = http,https filter = nginx-botsearch logpath = /var/log/nginx/access.log maxretry = 2
# Recargar después de editar systemctl reload fail2ban
# Ver estado general fail2ban-client status # Ver estado de una jail específica fail2ban-client status sshd # Ver IPs baneadas actualmente fail2ban-client status sshd | grep "Banned IP" # Desbanear una IP manualmente fail2ban-client set sshd unbanip 123.456.789.0 # Banear una IP manualmente fail2ban-client set sshd banip 123.456.789.0 # Ver el log de fail2ban tail -f /var/log/fail2ban.log # Recargar configuración sin reiniciar fail2ban-client reload # Probar un filtro contra un log fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf
Crear un filtro para banear IPs que busquen rutas típicas de ataques. Archivo en /etc/fail2ban/filter.d/nginx-custom.conf
# /etc/fail2ban/filter.d/nginx-custom.conf [Definition] failregex = ^<HOST> .* "(GET|POST) /(wp-admin|phpmyadmin|\.env| xmlrpc\.php|wp-login\.php|\.git/config) HTTP/.*" (200|301|302|404|403) ignoreregex =
# Agregar jail en jail.local [nginx-custom] enabled = true port = http,https filter = nginx-custom logpath = /var/log/nginx/access.log maxretry = 1 bantime = 604800 # 7 días para bots agresivos
# Crear usuario con directorio home adduser mi_usuario # interactivo, crea home useradd -m -s /bin/bash mi_usuario # no interactivo # Cambiar contraseña passwd mi_usuario # Crear usuario sin acceso a shell (para servicios) useradd -r -s /sbin/nologin mi_servicio # Ver todos los usuarios del sistema cat /etc/passwd | grep -v nologin | grep -v false # Ver usuarios con acceso SSH real grep "bash\|sh$" /etc/passwd # Eliminar usuario (sin borrar su home) userdel mi_usuario # Eliminar usuario Y su directorio home userdel -r mi_usuario # Bloquear usuario (sin eliminarlo) usermod -L mi_usuario # Desbloquear usermod -U mi_usuario
# Agregar usuario al grupo sudo usermod -aG sudo mi_usuario # Verificar grupos del usuario groups mi_usuario id mi_usuario # Editar sudoers de forma segura (SIEMPRE con visudo) visudo # O crear un archivo específico en /etc/sudoers.d/ visudo -f /etc/sudoers.d/mi_usuario
# Ejemplos de reglas en sudoers # Acceso sudo completo mi_usuario ALL=(ALL:ALL) ALL # Sudo sin contraseña (solo para casos específicos) deploy ALL=(ALL) NOPASSWD: ALL # Permitir solo comandos específicos deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx, \ /usr/bin/systemctl reload nginx
Nunca uses NOPASSWD: ALL en producción salvo que sea un usuario de deploy muy controlado. Es un riesgo de seguridad grande.
# Crear grupo groupadd webdev # Agregar usuario a grupo usermod -aG webdev mi_usuario # Ver grupos existentes cat /etc/group getent group webdev # Dar al grupo acceso a un directorio web chown -R www-data:webdev /var/www/mi-sitio chmod -R 775 /var/www/mi-sitio chmod g+s /var/www/mi-sitio # archivos nuevos heredan el grupo
El flag g+s (setgid) en un directorio hace que todos los archivos creados dentro hereden el grupo del directorio, no el del usuario que los crea.
Redirige un puerto del servidor al tu máquina local. Útil para acceder a bases de datos, paneles de administración o servicios que no están expuestos públicamente.
# Sintaxis: -L puerto_local:host_remoto:puerto_remoto # Acceder a MariaDB del VPS desde tu máquina local ssh -L 3307:127.0.0.1:3306 usuario@ip-del-vps # Ahora conectás tu cliente MySQL a localhost:3307 # Acceder a una app corriendo en el VPS (sin exponer el puerto) ssh -L 8080:127.0.0.1:3000 usuario@ip-del-vps # Ahora abrís http://localhost:8080 en tu navegador # Túnel en background (sin abrir shell) ssh -N -L 3307:127.0.0.1:3306 usuario@ip-del-vps & # Con alias del SSH config ssh -N -L 3307:127.0.0.1:3306 donweb
Es la forma más segura de trabajar con MariaDB — nunca exponer el puerto 3306 a internet, siempre via túnel SSH.
# Sintaxis: -R puerto_remoto:host_local:puerto_local # Exponer tu app local (puerto 3000) en el VPS (puerto 8080) ssh -R 8080:localhost:3000 usuario@ip-del-vps # Útil para mostrar trabajo en progreso sin deploy ssh -R 8080:localhost:3000 -N donweb
Para que funcione, el VPS debe tener GatewayPorts yes en sshd_config si querés que sea accesible desde internet (no solo desde el propio servidor).
# Crear proxy SOCKS5 en tu puerto local 1080 ssh -D 1080 -N usuario@ip-del-vps # En background ssh -D 1080 -N -f donweb # Usar el proxy con curl curl --socks5 localhost:1080 https://ejemplo.com
Configurable en el navegador como proxy SOCKS5 → localhost:1080. Todo el tráfico sale con la IP del VPS.
Mosh mantiene la sesión activa aunque cambie tu IP o se corte la conexión momentáneamente. Ideal para conexiones móviles o WiFi inestable.
# Instalar en el servidor apt install mosh # Conectar (desde tu máquina, con mosh instalado) mosh usuario@ip-del-vps # Con puerto SSH personalizado mosh --ssh="ssh -p 2222" usuario@ip-del-vps
Mosh usa UDP (puertos 60000-61000). Asegurate de tenerlos abiertos en el firewall si lo usás.
Seguí este orden al configurar un servidor nuevo. Cada ítem reduce significativamente la superficie de ataque.
apt update && apt upgrade -y antes de cualquier otra cosa.PasswordAuthentication no en sshd_config.PermitRootLogin no en sshd_config.apt install unattended-upgrades.systemctl list-units --state=running y parar lo que no se use.ss -tulnp para ver qué está escuchando.# Ver todos los puertos escuchando ss -tulnp netstat -tulnp # Ver conexiones activas ss -tnp state established # Revisar usuarios logueados ahora who w # Historial de accesos SSH last -20 lastb -20 # intentos fallidos # Actividad reciente en el sistema journalctl --since "1 hour ago" -p err # Archivos modificados recientemente find /etc -mtime -1 -type f 2>/dev/null find /var/www -mtime -1 -name "*.php" 2>/dev/null # Procesos corriendo como root ps aux | grep "^root" # Tareas cron de todos los usuarios for u in $(cut -f1 -d: /etc/passwd); do \ crontab -u $u -l 2>/dev/null; done
Recibir un email cada vez que alguien se conecta por SSH. Se agrega al perfil del usuario.
# Instalar mailutils si no está apt install mailutils # Agregar al final de /etc/profile o ~/.bashrc del usuario # (o en /etc/ssh/sshrc para que aplique solo a SSH)
# /etc/ssh/sshrc — se ejecuta al conectarse por SSH echo "SSH login: $(whoami) desde $(echo $SSH_CLIENT \ | awk '{print $1}') el $(date)" \ | mail -s "SSH Login en VPS" tu@email.com
Alternativa más completa: instalar logwatch para recibir un resumen diario de toda la actividad del servidor.