Teoria Bash 02: Automatització amb SSH

1. Introducció a SSH

SSH (Secure Shell) és un protocol de xarxa que permet:

Connexió bàsica

# Connectar a un servidor
ssh usuari@servidor.exemple.cat

# Connectar amb un port específic
ssh -p 2222 usuari@servidor.exemple.cat

# Executar una ordre remota i sortir
ssh usuari@servidor.exemple.cat "ls -la /var/www"

2. Claus SSH: Autenticació sense contrasenya

Les claus SSH permeten autenticar-se sense introduir contrasenya cada vegada.

Tipus de claus

Generar claus SSH

# Generar clau Ed25519 (recomanat)
ssh-keygen -t ed25519 -C "el_teu_email@exemple.cat"

# Generar clau RSA (si Ed25519 no està disponible)
ssh-keygen -t rsa -b 4096 -C "el_teu_email@exemple.cat"

# Durant la generació et demanarà:
# 1. On guardar la clau (per defecte: ~/.ssh/id_ed25519)
# 2. Passphrase (opcional però recomanada per seguretat extra)

Important:

Copiar la clau pública a un servidor

# Mètode 1: Usar ssh-copy-id (més fàcil)
ssh-copy-id usuari@servidor.exemple.cat

# Mètode 2: Manual
cat ~/.ssh/id_ed25519.pub | ssh usuari@servidor.exemple.cat "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

# Mètode 3: Copiar i enganxar manualment
# 1. Mostrar la clau pública
cat ~/.ssh/id_ed25519.pub

# 2. Al servidor, afegir-la a:
#    ~/.ssh/authorized_keys

Verificar la connexió

Després de copiar la clau:

# Hauria de connectar sense demanar contrasenya
ssh usuari@servidor.exemple.cat

# Si encara demana contrasenya, comprovar permisos:
# Al servidor:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

Configuració SSH avançada

Fitxer ~/.ssh/config:

# Servidor de producció
Host produccio
    HostName 192.168.1.100
    User administrador
    Port 22
    IdentityFile ~/.ssh/id_ed25519_prod

# Servidor de desenvolupament
Host devel
    HostName dev.exemple.cat
    User developer
    Port 2222
    IdentityFile ~/.ssh/id_ed25519

# Tots els servidors de la xarxa interna
Host 192.168.1.*
    User admin
    StrictHostKeyChecking no
    UserKnownHostsFile=/dev/null

Amb aquesta configuració:

# En lloc de:
ssh -p 2222 developer@dev.exemple.cat

# Pots fer:
ssh devel

3. Execució remota de ordres

ordre simple

# Executar i mostrar el resultat
ssh servidor "uptime"

# Executar múltiples ordres
ssh servidor "cd /var/www && ls -la && df -h"

# Capturar el resultat en una variable
resultat=$(ssh servidor "hostname")
echo "Nom del servidor: $resultat"

ordres amb variables locals

# Variable local que es passa al servidor
DIRECTORI="/var/log"
ssh servidor "ls -la $DIRECTORI"

# Millor: usar cometes dobles per expandir localment
ssh servidor "du -sh $DIRECTORI"

# O cometes simples per expandir al servidor
ssh servidor 'du -sh $HOME'

Script complet amb execució remota

#!/bin/bash

SERVIDORS=("web1.exemple.cat" "web2.exemple.cat" "db.exemple.cat")

for servidor in "${SERVIDORS[@]}"; do
    echo "=== Informació de $servidor ==="
    
    # Obtenir informació del sistema
    ssh "$servidor" << 'EOF'
        echo "Hostname: $(hostname)"
        echo "Uptime: $(uptime -p)"
        echo "Memòria lliure: $(free -h | grep Mem | awk '{print $4}')"
        echo "Disc lliure: $(df -h / | tail -1 | awk '{print $4}')"
        echo "Càrrega: $(cat /proc/loadavg | awk '{print $1, $2, $3}')"
EOF
    echo ""
done

Here documents amb SSH

# Executar múltiples ordres conservant format
ssh servidor << 'EOF'
    cd /var/www
    for file in *.log; do
        echo "Processant $file"
        gzip "$file"
    done
    echo "Compressió completada"
EOF

4. sshFS: Muntar sistemes de fitxers remots

sshFS permet muntar un directori remot com si fos local.

Instal·lació

# Debian/Ubuntu
sudo apt install sshfs

# Fedora/RHEL
sudo dnf install fuse-sshfs

# Afegir l'usuari al grup fuse
sudo usermod -a -G fuse $USER
# Cal tancar sessió i tornar a entrar

Ús bàsic

# Muntar un directori remot
sshfs usuari@servidor:/ruta/remota /ruta/local

# Exemple
mkdir ~/mnt_remot
sshfs root@servidor.exemple.cat:/var/www ~/mnt_remot

# Ara pots accedir als fitxers remots:
ls ~/mnt_remot
cat ~/mnt_remot/index.html

Opcions útils

# Muntar amb opcions específiques
sshfs usuari@servidor:/remot /local \
    -o reconnect \              # Reconnectar automàticament
    -o ServerAliveInterval=15 \ # Keep-alive cada 15s
    -o ServerAliveCountMax=3 \  # Màxim 3 intents
    -o compression=yes \        # Comprimir dades
    -o allow_other \            # Permetre accés a altres usuaris
    -o default_permissions      # Usar permisos locals

# Muntar en mode només lectura
sshfs usuari@servidor:/remot /local -o ro

Desmuntar

# Desmuntar normalment
fusermount -u ~/mnt_remot

# O amb umount (si tens permisos)
sudo umount ~/mnt_remot

# Forçar desmuntatge si està ocupat
fusermount -uz ~/mnt_remot

Script automatitzat amb sshFS

#!/bin/bash

SERVIDOR="backup.exemple.cat"
USUARI="admin"
DIRECTORI_REMOT="/backups"
PUNT_MUNTATGE="$HOME/backups_remots"

muntar_backup() {
    # Crear punt de muntatge si no existeix
    mkdir -p "$PUNT_MUNTATGE"
    
    # Comprovar si ja està muntat
    if mountpoint -q "$PUNT_MUNTATGE"; then
        echo "Ja està muntat"
        return 0
    fi
    
    echo "Muntant $SERVIDOR:$DIRECTORI_REMOT..."
    sshfs "$USUARI@$SERVIDOR:$DIRECTORI_REMOT" "$PUNT_MUNTATGE" \
        -o reconnect,ServerAliveInterval=15
    
    if [ $? -eq 0 ]; then
        echo "Muntat correctament a $PUNT_MUNTATGE"
        return 0
    else
        echo "Error muntant el sistema remot"
        return 1
    fi
}

desmuntar_backup() {
    if ! mountpoint -q "$PUNT_MUNTATGE"; then
        echo "No està muntat"
        return 0
    fi
    
    echo "Desmuntant $PUNT_MUNTATGE..."
    fusermount -u "$PUNT_MUNTATGE"
    
    if [ $? -eq 0 ]; then
        echo "Desmuntat correctament"
        return 0
    else
        echo "Error desmuntant. Provant amb força..."
        fusermount -uz "$PUNT_MUNTATGE"
        return $?
    fi
}

# Ús
case "$1" in
    muntar)
        muntar_backup
        ;;
    desmuntar)
        desmuntar_backup
        ;;
    *)
        echo "Ús: $0 {muntar|desmuntar}"
        exit 1
        ;;
esac

5. Transferència de fitxers

scp (Secure Copy)

# Copiar fitxer local → remot
scp fitxer.txt usuari@servidor:/ruta/destí/

# Copiar fitxer remot → local
scp usuari@servidor:/ruta/fitxer.txt .

# Copiar directori recursivament
scp -r directori/ usuari@servidor:/ruta/destí/

# Amb port específic
scp -P 2222 fitxer.txt usuari@servidor:/destí/

# Copiar entre dos servidors (des de la màquina local)
scp usuari1@servidor1:/fitxer usuari2@servidor2:/destí/

rsync (sincronització més intel·ligent)

# Sincronitzar directori local → remot
rsync -avz directori/ usuari@servidor:/destí/

# Opcions útils:
# -a: mode arxiu (preserva permisos, timestamps, etc.)
# -v: verbós
# -z: compressió
# -P: mostra progrés i permet reprendre
# --delete: eliminar fitxers al destí que no existeixen a l'origen

# Sincronització bidireccional
rsync -avz --delete local/ usuari@servidor:/remot/
rsync -avz --delete usuari@servidor:/remot/ local/

# Excloure fitxers/directoris
rsync -avz --exclude='*.log' --exclude='tmp/' local/ servidor:/remot/

6. Gestió d'errors i timeouts

#!/bin/bash

# Establir timeout per connexió SSH
ssh -o ConnectTimeout=10 usuari@servidor "ordre"

# Comprovar si SSH està disponible
if ssh -o ConnectTimeout=5 -o BatchMode=yes usuari@servidor exit 2>/dev/null; then
    echo "Servidor accessible"
else
    echo "Servidor no accessible"
fi

# Executar ordre amb gestió d'errors completa
executar_remot() {
    local servidor=$1
    local ordre=$2
    
    echo "Executant a $servidor: $ordre"
    
    if ssh -o ConnectTimeout=10 -o BatchMode=yes "$servidor" "$ordre"; then
        echo "✓ ordre executada correctament"
        return 0
    else
        local codi=$?
        echo "✗ Error executant ordre (codi: $codi)"
        return $codi
    fi
}

7. Consideracions de seguretat

Bones pràctiques

  1. Usar claus en lloc de contrasenyes

    # Deshabilitar autenticació per contrasenya al servidor
    # /etc/ssh/sshd_config
    PasswordAuthentication no
    
  2. Protegir les claus privades

    chmod 600 ~/.ssh/id_ed25519
    chmod 644 ~/.ssh/id_ed25519.pub
    chmod 700 ~/.ssh
    
  3. Usar passphrase a les claus

    # Afegir clau a l'agent SSH per no haver de introduir passphrase cada vegada
    eval $(ssh-agent)
    ssh-add ~/.ssh/id_ed25519
    
  4. Limitar accés per IP (si és possible)

    # ~/.ssh/authorized_keys
    from="192.168.1.0/24" ssh-ed25519 AAAAC3...
    
  5. Usar claus diferents per a diferents finalitats

    ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_producció
    ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_desenvolupament
    

Resum