SSH Avanzado: Tunnels, Configuración y Técnicas para Administradores
SSH (Secure Shell) es mucho más que una simple herramienta para conectarse remotamente a servidores. Es una navaja suiza de la administración de sistemas que ofrece capacidades avanzadas de túneles, transferencia de archivos, gestión de claves y proxy que todo administrador de sistemas debe dominar.
Fundamentos Avanzados de SSH
Arquitectura del Protocolo SSH
SSH opera en tres capas principales:
- Transport Layer Protocol: Encriptación, integridad y autenticación del servidor
- User Authentication Protocol: Autenticación del cliente
- Connection Protocol: Multiplexado de múltiples canales
1
2
3
4
5
| # Ver versión y capacidades SSH
ssh -V
ssh -Q cipher # Algoritmos de cifrado disponibles
ssh -Q mac # Algoritmos MAC disponibles
ssh -Q kex # Algoritmos de intercambio de claves
|
Configuración Avanzada del Cliente SSH
~/.ssh/config - El Archivo Maestro
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
| # ~/.ssh/config
# Configuración global por defecto
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
TCPKeepAlive yes
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600
HashKnownHosts yes
VisualHostKey yes
AddKeysToAgent yes
UseKeychain yes
# Servidor de desarrollo
Host dev
HostName 192.168.1.100
User developer
Port 2222
IdentityFile ~/.ssh/dev_rsa
LocalForward 3306 localhost:3306
LocalForward 8080 localhost:8080
ForwardAgent yes
# Servidor de producción con bastion host
Host prod
HostName 10.0.1.50
User admin
IdentityFile ~/.ssh/prod_ed25519
ProxyJump bastion
StrictHostKeyChecking yes
# Bastion host
Host bastion
HostName bastion.empresa.com
User jump_user
Port 443
IdentityFile ~/.ssh/bastion_key
DynamicForward 1080
# Conexión a través de proxy SOCKS
Host internal-server
HostName 10.0.2.100
User sysadmin
ProxyCommand nc -X 5 -x 127.0.0.1:1080 %h %p
# Servidor con configuración específica de seguridad
Host secure-server
HostName secure.empresa.com
User security_admin
IdentityFile ~/.ssh/secure_ed25519
KexAlgorithms curve25519-sha256@libssh.org
Ciphers chacha20-poly1305@openssh.com
MACs hmac-sha2-256-etm@openssh.com
PubkeyAuthentication yes
PasswordAuthentication no
ChallengeResponseAuthentication no
GSSAPIAuthentication no
|
Configuración Avanzada del Servidor SSH
/etc/ssh/sshd_config - Hardening de Producción
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
| # /etc/ssh/sshd_config
# Configuración básica de seguridad
Protocol 2
Port 2222 # Cambiar puerto por defecto
AddressFamily inet # Solo IPv4 (o inet6 para IPv6)
ListenAddress 0.0.0.0
# Autenticación robusta
PermitRootLogin no # Prohibir login directo como root
MaxAuthTries 3 # Máximo 3 intentos de autenticación
MaxSessions 4 # Máximo 4 sesiones por conexión
MaxStartups 10:30:60 # Control de conexiones concurrentes
# Configuración de claves
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
PasswordAuthentication no # Solo autenticación por clave
PermitEmptyPasswords no
ChallengeResponseAuthentication no
# Algoritmos criptográficos modernos
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group16-sha512
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256,hmac-sha2-512
# Timeouts y conexiones
ClientAliveInterval 300 # Ping cliente cada 5 minutos
ClientAliveCountMax 2 # Desconectar después de 2 pings fallidos
TCPKeepAlive no # Usar ClientAlive en lugar de TCP keepalive
LoginGraceTime 30 # 30 segundos para autenticarse
# Restricciones de acceso
AllowUsers developer sysadmin backup # Solo estos usuarios pueden conectar
DenyUsers guest test # Usuarios explícitamente denegados
AllowGroups ssh-users admin # Solo miembros de estos grupos
# Características de seguridad adicionales
X11Forwarding no # Deshabilitar forwarding de X11
AllowTcpForwarding local # Solo forwarding local
PermitTunnel no # Deshabilitar túneles de red
GatewayPorts no # No permitir gateway ports
PermitUserEnvironment no # No permitir variables de entorno custom
# Logging detallado
SyslogFacility AUTH
LogLevel VERBOSE # Log detallado para auditoría
# Banner informativo
Banner /etc/ssh/banner.txt
PrintMotd no
PrintLastLog yes
# Configuración de subsistemas
Subsystem sftp internal-sftp
# Match blocks para configuraciones específicas
Match Group sftp-only
ChrootDirectory /home/%u
ForceCommand internal-sftp
AllowTcpForwarding no
X11Forwarding no
Match User backup
AllowTcpForwarding no
X11Forwarding no
PermitTTY no
ForceCommand /usr/local/bin/backup-only.sh
|
Gestión Avanzada de Claves SSH
Tipos de Claves y Cuándo Usarlas
1
2
3
4
5
6
7
8
9
10
11
| # Ed25519 - Recomendada para uso general (más segura y rápida)
ssh-keygen -t ed25519 -C "usuario@empresa.com"
# RSA 4096 bits - Para compatibilidad con sistemas antiguos
ssh-keygen -t rsa -b 4096 -C "usuario@empresa.com"
# ECDSA - Para entornos con restricciones específicas
ssh-keygen -t ecdsa -b 521 -C "usuario@empresa.com"
# Generar clave con configuración específica
ssh-keygen -t ed25519 -f ~/.ssh/proyecto_especifico -C "proyecto@empresa.com" -N "passphrase_segura"
|
Gestión de Múltiples Claves
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # ~/.ssh/config para múltiples identidades
Host github.com-personal
HostName github.com
User git
IdentityFile ~/.ssh/personal_ed25519
IdentitiesOnly yes
Host github.com-work
HostName github.com
User git
IdentityFile ~/.ssh/work_ed25519
IdentitiesOnly yes
# Uso:
git clone git@github.com-personal:usuario/repo-personal.git
git clone git@github.com-work:empresa/repo-trabajo.git
|
SSH Agent y Forwarding Seguro
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # Iniciar ssh-agent y cargar claves
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
ssh-add -t 3600 ~/.ssh/work_key # Clave que expira en 1 hora
# Ver claves cargadas
ssh-add -l
ssh-add -L # Con claves públicas completas
# Eliminar claves específicas
ssh-add -d ~/.ssh/work_key
ssh-add -D # Eliminar todas las claves
# Forwarding de agente seguro
ssh -A user@server1 # Desde server1 puedes usar tus claves locales
# CUIDADO: Solo usar Agent Forwarding en servidores confiables
|
Autorized Keys Avanzado
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # ~/.ssh/authorized_keys con restricciones
# Restricción por comando
command="/usr/local/bin/backup.sh" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... backup@servidor
# Restricción por IP origen
from="192.168.1.0/24" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... admin@oficina
# Sin shell, solo port forwarding
no-pty,no-shell,no-agent-forwarding,no-X11-forwarding,command="echo 'Solo túneles permitidos'" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... tunnel@cliente
# Solo SFTP
restrict,command="internal-sftp" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... sftp@usuario
# Combinando múltiples restricciones
no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,command="/usr/local/bin/rsync-only.sh" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... rsync@backup
|
SSH Tunneling - Técnicas Avanzadas
Local Port Forwarding
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # Túnel básico - acceder a MySQL remoto localmente
ssh -L 3306:localhost:3306 user@database-server
# Múltiples túneles en una conexión
ssh -L 3306:localhost:3306 -L 5432:localhost:5432 -L 6379:localhost:6379 user@server
# Túnel a través de servidor intermedio
ssh -L 8080:internal-server:80 user@jump-server
# Túnel persistente en background
ssh -f -N -L 8080:localhost:80 user@server
# -f: background, -N: no ejecutar comando remoto
# Túnel con bind a todas las interfaces (CUIDADO: implicaciones de seguridad)
ssh -L 0.0.0.0:8080:localhost:80 user@server
|
Remote Port Forwarding
1
2
3
4
5
6
7
8
9
10
| # Exponer servicio local al servidor remoto
ssh -R 8080:localhost:3000 user@public-server
# El puerto 8080 del servidor remoto apunta a tu puerto 3000 local
# Túnel reverso para acceso desde internet
ssh -R 2222:localhost:22 user@vps-publico
# Ahora puedes conectarte a tu máquina local desde el VPS: ssh -p 2222 localhost
# Múltiples túneles reversos
ssh -R 80:localhost:8000 -R 443:localhost:8443 user@proxy-server
|
Dynamic Port Forwarding (SOCKS Proxy)
1
2
3
4
5
6
7
8
9
10
11
12
13
| # Crear proxy SOCKS en puerto local
ssh -D 1080 user@server
# Usar el proxy SOCKS
curl --proxy socks5://localhost:1080 http://internal-website.com
firefox & # Configurar proxy SOCKS5 en 127.0.0.1:1080
# SOCKS proxy con autenticación
ssh -D 127.0.0.1:1080 -N -f user@server
# Usar proxy desde línea de comandos
export ALL_PROXY=socks5://127.0.0.1:1080
curl http://internal-resource.com
|
Tunneling a Través de Múltiples Saltos
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # Método 1: ProxyJump (OpenSSH 7.3+)
ssh -J jump1,jump2 user@final-destination
# Método 2: ProxyCommand con netcat
ssh -o ProxyCommand="ssh -W %h:%p jump-server" user@destination
# Método 3: Túnel encadenado manual
ssh -L 2222:destination:22 user@jump-server
ssh -p 2222 user@localhost # Se conecta realmente a destination
# Automatización con config
Host destination
ProxyJump jump1,jump2
HostName 10.0.3.100
User admin
|
Técnicas Avanzadas de Administración
SSH Multiplexing - Reutilizar Conexiones
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # Configuración en ~/.ssh/config
Host *
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600
# Crear directorio para sockets
mkdir -p ~/.ssh/sockets
# Primera conexión establece el master
ssh user@server
# Conexiones posteriores reutilizan el socket
ssh user@server # Esta conexión es instantánea
scp file.txt user@server:~/ # También instantánea
|
Transferencia de Archivos Avanzada
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| # SCP con opciones avanzadas
scp -C -r -p source/ user@server:destination/
# -C: compresión, -r: recursivo, -p: preservar timestamps
# SCP a través de jump server
scp -o 'ProxyJump jump-server' file.txt user@destination:~/
# SFTP batch mode
sftp -b commands.txt user@server
# commands.txt:
# lcd /local/path
# cd /remote/path
# put *.log
# quit
# rsync sobre SSH (más eficiente para sincronización)
rsync -avz -e "ssh -p 2222" --progress source/ user@server:destination/
rsync -avz -e "ssh -o ProxyJump=jump" --delete source/ user@destination:dest/
|
Ejecución Remota de Comandos
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| # Comando simple
ssh user@server 'df -h && free -m'
# Comando con variables locales
LOCAL_VAR="valor"
ssh user@server "echo $LOCAL_VAR > /tmp/remote_file"
# Script complejo remoto
ssh user@server 'bash -s' < local_script.sh
# Comando con pseudo-terminal (para comandos interactivos)
ssh -t user@server 'sudo systemctl restart nginx'
# Ejecutar múltiples comandos
ssh user@server << 'EOF'
cd /var/log
sudo tail -f nginx/access.log | grep "ERROR"
EOF
|
SSH para Administración de Clusters
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| # Ejecutar comando en múltiples servidores
for server in web{1..5}.ejemplo.com; do
echo "=== $server ==="
ssh "$server" 'uptime && df -h / | tail -1'
done
# Usando GNU parallel para ejecución paralela
parallel -j 10 ssh {} 'hostname && uptime' ::: server{1..20}.com
# Distribución de claves a múltiples servidores
for server in $(cat servidores.txt); do
ssh-copy-id user@$server
done
# Script de mantenimiento distribuido
#!/bin/bash
SERVERS="web1 web2 db1 db2"
COMMAND="sudo apt update && sudo apt upgrade -y"
for server in $SERVERS; do
echo "Ejecutando en $server..."
ssh -t "$server" "$COMMAND" &
done
wait
echo "Mantenimiento completado en todos los servidores"
|
Automatización y Scripts SSH
Script de Backup Automatizado
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
| #!/bin/bash
# backup_ssh.sh - Backup automatizado con SSH
set -euo pipefail
# Configuración
BACKUP_USER="backup"
BACKUP_HOST="backup-server.com"
BACKUP_PATH="/backups/$(hostname)"
LOCAL_BACKUP_DIR="/var/backups/local"
LOG_FILE="/var/log/ssh_backup.log"
# Función de logging
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
# Crear directorio local si no existe
mkdir -p "$LOCAL_BACKUP_DIR"
log "Iniciando backup SSH para $(hostname)"
# Crear tar comprimido de directorios importantes
BACKUP_FILE="backup_$(hostname)_$(date +%Y%m%d_%H%M%S).tar.gz"
tar -czf "$LOCAL_BACKUP_DIR/$BACKUP_FILE" \
/etc \
/home \
/var/www \
--exclude='/home/*/.cache' \
--exclude='/var/www/*/cache'
log "Archivo local creado: $BACKUP_FILE"
# Transferir al servidor de backup
if scp "$LOCAL_BACKUP_DIR/$BACKUP_FILE" "$BACKUP_USER@$BACKUP_HOST:$BACKUP_PATH/"; then
log "Backup transferido exitosamente"
# Limpiar archivo local después de transferencia exitosa
rm "$LOCAL_BACKUP_DIR/$BACKUP_FILE"
log "Archivo local eliminado"
# Limpiar backups antiguos en servidor remoto (mantener últimos 7 días)
ssh "$BACKUP_USER@$BACKUP_HOST" "find $BACKUP_PATH -name '*.tar.gz' -mtime +7 -delete"
log "Backups antiguos eliminados del servidor remoto"
else
log "ERROR: Fallo en transferencia de backup"
exit 1
fi
log "Proceso de backup completado"
|
Monitoreo de Servidores con SSH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
| #!/bin/bash
# monitor_servers.sh - Monitoreo distribuido
SERVERS="web1.com web2.com db1.com db2.com"
ALERT_EMAIL="admin@empresa.com"
THRESHOLD_CPU=80
THRESHOLD_MEM=90
THRESHOLD_DISK=85
check_server() {
local server=$1
local status_file="/tmp/status_$server"
# Obtener métricas del servidor
ssh "$server" << 'EOF' > "$status_file" 2>&1
# CPU usage
CPU=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | sed 's/%us,//')
# Memory usage
MEM=$(free | grep Mem | awk '{printf "%.0f", $3/$2 * 100.0}')
# Disk usage
DISK=$(df / | tail -1 | awk '{print $5}' | sed 's/%//')
# Load average
LOAD=$(uptime | awk -F'load average:' '{print $2}' | awk '{print $1}' | sed 's/,//')
echo "CPU:$CPU MEM:$MEM DISK:$DISK LOAD:$LOAD"
EOF
if [ $? -eq 0 ]; then
cat "$status_file"
else
echo "ERROR: No se pudo conectar a $server"
fi
rm -f "$status_file"
}
echo "=== Monitoreo de Servidores $(date) ==="
for server in $SERVERS; do
echo -n "$server: "
check_server "$server"
done
|
Deployment Automatizado
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
| #!/bin/bash
# deploy.sh - Deployment automático con SSH
set -e
APP_NAME="mi-aplicacion"
SERVERS=("web1.com" "web2.com" "web3.com")
DEPLOY_USER="deploy"
APP_PATH="/var/www/$APP_NAME"
BACKUP_PATH="/var/backups/$APP_NAME"
GIT_REPO="git@github.com:empresa/mi-aplicacion.git"
BRANCH="main"
deploy_to_server() {
local server=$1
echo "=== Desplegando en $server ==="
ssh "$DEPLOY_USER@$server" << EOF
set -e
# Crear backup de la versión actual
if [ -d "$APP_PATH" ]; then
sudo cp -r "$APP_PATH" "$BACKUP_PATH/backup_\$(date +%Y%m%d_%H%M%S)"
echo "Backup creado en $server"
fi
# Clonar o actualizar repositorio
if [ -d "$APP_PATH/.git" ]; then
cd "$APP_PATH"
sudo git fetch origin
sudo git checkout "$BRANCH"
sudo git pull origin "$BRANCH"
else
sudo rm -rf "$APP_PATH"
sudo git clone "$GIT_REPO" "$APP_PATH"
cd "$APP_PATH"
sudo git checkout "$BRANCH"
fi
# Instalar dependencias
sudo npm install --production
# Ejecutar migraciones si existen
if [ -f "package.json" ] && grep -q "migrate" package.json; then
sudo npm run migrate
fi
# Reiniciar servicios
sudo systemctl restart "$APP_NAME"
sudo systemctl reload nginx
echo "Deployment completado en $server"
EOF
}
# Verificar que el branch existe
if ! git ls-remote --heads origin "$BRANCH" | grep -q "$BRANCH"; then
echo "ERROR: Branch '$BRANCH' no existe en el repositorio remoto"
exit 1
fi
# Desplegar en todos los servidores
for server in "${SERVERS[@]}"; do
deploy_to_server "$server" &
done
# Esperar a que todos los deployments terminen
wait
echo "=== Deployment completado en todos los servidores ==="
# Verificar que los servicios están funcionando
echo "=== Verificando servicios ==="
for server in "${SERVERS[@]}"; do
echo -n "$server: "
if ssh "$DEPLOY_USER@$server" "curl -s -o /dev/null -w '%{http_code}' http://localhost"; then
echo "OK"
else
echo "ERROR - Servicio no responde"
fi
done
|
Troubleshooting SSH
Diagnóstico de Conexiones
1
2
3
4
5
6
7
8
9
10
11
| # Debug detallado de conexión SSH
ssh -vvv user@server
# Test de conectividad específica
ssh -o ConnectTimeout=5 -o BatchMode=yes user@server echo "OK"
# Verificar configuración sin conectar
ssh -T user@server
# Test de autenticación por clave
ssh -o PreferredAuthentications=publickey -o PasswordAuthentication=no user@server
|
Problemas Comunes y Soluciones
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| # Permission denied (publickey) - verificar permisos
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_rsa
chmod 644 ~/.ssh/id_rsa.pub
chmod 600 ~/.ssh/authorized_keys
# Servidor: verificar permisos del directorio home
chmod 755 /home/username
chmod 700 /home/username/.ssh
chmod 600 /home/username/.ssh/authorized_keys
# Verificar que SELinux no está bloqueando
sudo setsebool -P ssh_sysadm_login on
sudo restorecon -R ~/.ssh
# Regenerar host keys si están corruptos
sudo rm /etc/ssh/ssh_host_*
sudo dpkg-reconfigure openssh-server
|
Monitoring SSH en Tiempo Real
1
2
3
4
5
6
7
8
9
10
11
12
| # Ver conexiones SSH activas
sudo netstat -tnlp | grep :22
sudo ss -tnlp | grep :22
# Logs de SSH en tiempo real
sudo tail -f /var/log/auth.log | grep sshd
# Ver intentos de conexión fallidos
sudo grep "Failed password" /var/log/auth.log | tail -10
# Detectar ataques de fuerza bruta
sudo grep "Failed password" /var/log/auth.log | awk '{print $11}' | sort | uniq -c | sort -nr
|
Conclusión
SSH es una herramienta increíblemente poderosa que va mucho más allá de la simple conexión remota. Dominar sus capacidades avanzadas te permitirá:
- Crear túneles seguros para acceder a servicios internos
- Automatizar tareas de administración en múltiples servidores
- Implementar soluciones de seguridad robustas con autenticación por clave
- Optimizar workflows con multiplexing y configuraciones personalizadas
- Resolver problemas complejos de conectividad y acceso
La inversión en aprender estas técnicas avanzadas de SSH se traduce directamente en mayor eficiencia, seguridad y capacidades profesionales como administrador de sistemas.
Andrés Nuñez - t4ifi