Entrada

SSH Avanzado: Tunnels, Configuración y Técnicas para Administradores

SSH Avanzado: Tunnels, Configuración y Técnicas para Administradores

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:

  1. Transport Layer Protocol: Encriptación, integridad y autenticación del servidor
  2. User Authentication Protocol: Autenticación del cliente
  3. 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

Esta entrada está licenciada bajo CC BY 4.0 por el autor.