ProtectHome contro un seme TOTP: perché l’accesso VPN fallisce
Un codice corretto non aiuta un modulo PAM che non riesce ad aprire il seme. Scegli tra un drop-in e un percorso di stato scrivibile, poi controlla l’unità.
L’autenticatore mostra un nuovo codice a sei cifre, la VPN lo rifiuta e lo stesso codice si verifica senza problemi quando lo provi dalla tua shell. Quel divario è tutta la storia. Un modulo PAM che non riesce ad aprire il segreto di registrazione restituisce un errore che sembra identico a un codice sbagliato, e l’ambiente del servizio che nasconde le directory home è una causa comune. Prima di allentare qualsiasi permesso, decidi in quale delle tre situazioni ti trovi.
Quale dei tre guasti è questo?
Il primo è un segreto che il modulo non può leggere affatto. Il secondo è un segreto che può leggere ma non aggiornare. Il terzo è un codice davvero sbagliato, di solito per deriva dell’orologio o un valore riutilizzato.
Separali leggendo il journal del servizio e il registro di autenticazione del sistema per un tentativo fallito, poi conferma quale servizio PAM carica il plugin e quale riga di modulo usa quel servizio. Un messaggio generico di errore di autenticazione non li distingue, e nemmeno un test manuale riuscito, perché la tua shell non è l’ambiente del servizio.
La vista del file system dell’unità è il colpevole abituale quando fallisce solo il servizio.
systemctl show -p ProtectHome -p ProtectSystem -p ReadWritePaths [email protected]
ls -l /var/lib/vpn-otp/*/.google_authenticator 2>/dev/null | head -n 20Opzione A: spostare il segreto fuori dalla home
Quando il servizio nasconde /home e preferisci mantenere quella protezione, conserva il segreto dove può vederlo. Il modulo accetta un percorso secret= e un account user= che esegue le operazioni sui file, il che consente a un archivio centrale di servire molte identità.
Questa è l’opzione ordinata, ed è quella che richiede più cura, perché un archivio condiviso rende una singola directory il confine di sicurezza di ogni account.
auth required pam_google_authenticator.so secret=/var/lib/vpn-otp/${USER}/.google_authenticator user=vpn-otpControlla tre cose prima di fidarti: l’account esiste, il processo di lavoro del modulo può passare a esso e i file sono suoi con modalità restrittive. Non usare ~ né un percorso con variabile home insieme a user=, e non disattivare i controlli di proprietà del modulo per far funzionare un percorso. Mantieni intatti la fase della password e il resto dello stack.
Opzione B: lasciare il file e aprire un percorso
Se il segreto deve restare nella home dell’utente, l’unità ha bisogno di una vista che includa quella sola directory senza esporre ogni home sull’host. Un drop-in può aggiungere un mount bind strettamente limitato sotto una home isolata.
Questo mantiene la proprietà per utente come modello, al costo di una modifica all’unità che va rivista ogni volta che cambia la sandbox.
Due limiti sono facili da trascurare. Un percorso in sola lettura aggiunto sotto una directory che la sandbox nasconde non la espone, perché il genitore semplicemente non c’è. E il mount deve essere scrivibile se il modulo deve aggiornare lo stato, il che mette in gioco l’opzione successiva. Scrivi il drop-in come file separato invece di modificare l’unità pacchettizzata sotto /usr/lib, così un aggiornamento del pacchetto non lo annulla silenziosamente.
Opzione C: mantenere la sandbox e correggere le scritture di stato
Leggere il segreto è solo metà dell’autenticazione. Il modulo può riscrivere informazioni di deriva temporale, protezione dalla ripetizione o un contatore, e un file che supera il controllo di lettura può comunque fallire la scrittura.
Quel guasto sembra un codice sbagliato al tentativo successivo anziché a questo, perciò viene confuso con una deriva dell’orologio.
Dagli una directory scrivibile propria e indirizza lì il percorso del segreto, invece di rendere scrivibile tutta la home. Conferma il proprietario della directory, lo spazio libero del file system e se l’unità limita le scritture altrove. Poi prova due accessi consecutivi, perché il secondo dimostra il percorso di scrittura e il primo solo la lettura.
Verifica come il servizio, non come te stesso
Ogni controllo che conta deve essere eseguito dentro la sandbox dell’unità con l’identità dell’unità.
Un comando che riesce come root ti dice che il file esiste e nient’altro. Un accesso tramite il servizio reale ti dice se il modulo può leggere il segreto, usarlo e riscrivere ciò che gli serve.
Infine, mantieni intatto il requisito. Aggiungere un’opzione di password nulla, accettare errori di sola lettura o rimuovere la sandbox farebbero sparire il sintomo cambiando la politica. Un utente tenuto a presentare un codice deve comunque essere respinto quando il segreto è irraggiungibile.