Come configurare un bastion host SSH

Un bastion è un piccolo server che accetta SSH dall'esterno e lo inoltra solo ai tuoi server interni. Dai ai suoi utenti un gruppo con AllowTcpForwarding local, PermitOpen con l'elenco delle destinazioni permesse, PermitTTY no e ForceCommand /usr/sbin/nologin, dentro un blocco Match Group, con l'inoltro disattivato per tutti gli altri. Gli utenti si collegano poi con ssh -J user@bastion user@internal. Non impostare DisableForwarding yes per loro, perché blocca anche il salto.

Aggiornata il . Questa configurazione è stata provata con OpenSSH 10.0 su Debian 13, usando due container usa e getta come bastion e destinazione.

Com'è fatto

  • Un server piccolo e separato che esegue SSH e nient'altro: niente database, niente sito web, niente strumenti per sviluppatori.
  • Solo chiavi, con aggiornamenti di sicurezza automatici.
  • Nessuna chiave privata sopra. Con un salto non servono. Il login verso il server interno è cifrato a partire dalla tua macchina, e il bastion si limita a far passare i dati.
  • L'unico ingresso. I server interni devono accettare SSH solo dall'indirizzo del bastion, a livello di firewall.

Per il resto delle impostazioni SSH del server, vedi come mettere in sicurezza sshd_config.

Creare gli utenti

sudo groupadd jump
sudo useradd -m -G jump -s /bin/bash alice
sudo install -d -m 700 -o alice -g alice /home/alice/.ssh
sudo install -m 600 -o alice -g alice alice.pub /home/alice/.ssh/authorized_keys

Tieni il tuo account admin fuori dal gruppo jump, così hai ancora una shell.

La configurazione di sshd

# /etc/ssh/sshd_config.d/10-bastion.conf
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
AllowAgentForwarding no
AllowTcpForwarding no
AllowStreamLocalForwarding no
X11Forwarding no
PermitTunnel no
GatewayPorts no
LogLevel VERBOSE

Match Group jump
    AllowTcpForwarding local
    PermitOpen 10.0.1.10:22 10.0.1.11:22
    PermitTTY no
    ForceCommand /usr/sbin/nologin

Ogni impostazione del blocco ha il suo compito:

  • AllowTcpForwarding local permette i salti e -L, ma non -R.
  • PermitOpen elenca dove possono andare. Elenca ogni server: * può valere per un intero host o una porta, ma non per una parte di indirizzo come 10.0.1.*.
  • PermitTTY no e ForceCommand tolgono la shell. ForceCommand da solo non ferma l'inoltro, ed è ciò che permette al salto di funzionare qui.

Mettilo in un file a parte in /etc/ssh/sshd_config.d/, e prima controlla che nessun altro file imposti le stesse opzioni prima di lui: sshd tiene il primo valore che legge. Un blocco Match in un file così finisce dove finisce il file. Poi controlla e ricarica, lasciando aperta una sessione:

sudo sshd -t
sudo sshd -T -C user=alice,host=client,addr=203.0.113.10 | grep -iE '^(allowtcpforwarding|permitopen|permittty|forcecommand) '
sudo systemctl reload ssh
permittty no
allowtcpforwarding local
forcecommand /usr/sbin/nologin
permitopen 10.0.1.10:22 10.0.1.11:22

Il percorso di nologin può essere /sbin/nologin su altri sistemi, quindi verificalo con command -v nologin.

DisableForwarding è per altri utenti

DisableForwarding yes disattiva ogni inoltro e prevale sulle altre impostazioni, quindi è comodo per un gruppo che non deve mai inoltrare nulla. Non usarlo per gli utenti del salto. Un salto è un inoltro, e nella nostra prova è fallito con stdio forwarding failed.

Collegarsi attraverso il bastion

ssh -J alice@bastion.example.com user@10.0.1.10

Oppure, per scrivere meno, in ~/.ssh/config sulla tua macchina:

Host bastion
    HostName bastion.example.com
    User alice

Host app1
    HostName 10.0.1.10
    User user
    ProxyJump bastion

Poi esegui ssh app1. La tua chiave deve essere sia sul bastion sia sul server interno. Altro sul lato client in jump host e ProxyJump. Evita ForwardAgent, per i motivi descritti in rischi dell'inoltro dell'agent.

Verificare cosa viene rifiutato

Questi sono i risultati ottenuti con questa configurazione, e dovresti vedere gli stessi:

ProvaRisultato
ssh -J alice@bastion user@10.0.1.10Login riuscito sul server interno
ssh -W 10.0.1.10:80 alice@bastionstdio forwarding failed
ssh -tt alice@bastionPTY allocation request failed on channel 0
ssh alice@bastion idThis account is currently not available.
ssh -N -o ExitOnForwardFailure=yes -R 8080:localhost:22 alice@bastionError: remote port forwarding failed for listen port 8080

Log: cosa ottieni e cosa no

Già al livello predefinito ogni login viene registrato con il fingerprint della sua chiave, che ti dice chi si è connesso. LogLevel VERBOSE registra anche le chiavi rifiutate. Un login appare così:

Accepted publickey for alice from 203.0.113.10 port 51018 ssh2: ED25519 SHA256:NxFS05Q+k1zf...

Vengono registrate anche le destinazioni rifiutate:

Received request from 203.0.113.10 port 51036 to connect to host 10.0.1.10 port 80, but the request was denied.

Le destinazioni raggiunte con successo non vengono registrate, a livello INFO o VERBOSE. Lo abbiamo verificato: il log del bastion mostra che alice si è connessa, ma non verso quale server è andata poi. Nella nostra prova compariva solo a LogLevel DEBUG1, e il manuale avverte che i livelli di debug possono registrare dettagli privati degli utenti:

debug1: server_request_direct_tcpip: originator 127.0.0.1 port 65535, target 10.0.1.10 port 22

Quindi leggi entrambi i lati. Il log del server interno mostra chi ha fatto login e con quale chiave, con l'indirizzo del bastion come origine. Mettili insieme in base all'ora, e invia entrambi a una macchina esterna al bastion, perché un attaccante con root lì potrebbe modificare la copia locale. Vedi come controllare i tentativi di login per leggere questi log.

Togliere l'accesso

Togli la chiave pubblica dell'utente dal bastion, e perde questo percorso verso tutti i server che stanno dietro. Può comunque usare qualsiasi server interno che riesce a raggiungere in altro modo, quindi togli la chiave anche lì. Vedi come revocare una chiave.

Domande

Che differenza c'è tra bastion host e jump host?

Poca. Un jump host è qualsiasi server da cui passi per raggiungerne un altro. Un bastion è un jump host preparato e messo in sicurezza solo per quel compito, ed è l'unico ingresso dall'esterno.

Il bastion ha bisogno della mia chiave privata?

No. Con ProxyJump il login al server interno parte dal tuo computer ed è cifrato da un capo all'altro. Il bastion si limita a inoltrare la connessione.

Perché ssh -J fallisce con stdio forwarding failed?

Il bastion ha rifiutato l'inoltro. Controlla che AllowTcpForwarding lo consenta al tuo utente, che la destinazione sia in PermitOpen e che DisableForwarding non sia attivo.

Fonti

Altro in Tunnel e jump host

Tutte le guide