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 localpermette i salti e-L, ma non-R.PermitOpenelenca dove possono andare. Elenca ogni server:*può valere per un intero host o una porta, ma non per una parte di indirizzo come10.0.1.*.PermitTTY noeForceCommandtolgono la shell.ForceCommandda 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:
| Prova | Risultato |
|---|---|
ssh -J alice@bastion user@10.0.1.10 | Login riuscito sul server interno |
ssh -W 10.0.1.10:80 alice@bastion | stdio forwarding failed |
ssh -tt alice@bastion | PTY allocation request failed on channel 0 |
ssh alice@bastion id | This account is currently not available. |
ssh -N -o ExitOnForwardFailure=yes -R 8080:localhost:22 alice@bastion | Error: 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.