Area di amministrazione SaaS dietro la VPN: a quale indirizzo fidarsi?
L’origine vede l’indirizzo di uscita del tuo gateway VPN, non l’indirizzo del tunnel del portatile. Fidati solo di un’intestazione proxy configurata, poi vincola ogni operazione amministrativa.
Le pagine pubbliche di SaaS possono restare aperte a tutti mentre le operazioni amministrative che stanno dietro rifiutano chiunque sia fuori dalla VPN. Il meccanismo che lo rende possibile non è un menu nascosto né una pagina di accesso separata; è un controllo del percorso di rete che ha consegnato la richiesta. Se la prova dell’indirizzo è sbagliata, il controllo o esclude amministratori reali o ammette chiunque sappia digitare un’intestazione. Inizia da ciò che l’origine riceve davvero.
Ciò che l’origine riceve davvero dopo il tunnel
Quando un browser raggiunge il sito pubblico tramite una VPN che trasporta anche traffico internet, il gateway traduce la connessione prima che esca. Il SaaS vede l’indirizzo di uscita pubblico del gateway, di solito un indirizzo o un piccolo intervallo, e non vede mai l’indirizzo del tunnel del portatile. Per questo fallisce l’inserimento di 10.90.0.42 nella lista consentita, e per questo fallisce ancora più forte l’inserimento di tutto il RFC 1918: il secondo ammette ogni rete privata che possa raggiungerti. L’unico indirizzo che vale una condizione è quello che la tua organizzazione possiede davvero all’uscita.
curl -s https://ifconfig.me
dig +short admin.example.com
Il tunneling diviso rompe questa ipotesi in silenzio. Se la VPN trasporta solo route private dell’ufficio, la richiesta al nome host amministrativo pubblico esce dal provider locale e l’origine vede un indirizzo residenziale. Un’icona VPN connessa non prova nulla su questa destinazione precisa, quindi prova la destinazione, non il tunnel.
Fermati e correggi l’instradamento prima di scrivere qualsiasi politica se l’indirizzo di uscita varia per utente. Una condizione che corrisponde solo ad alcuni amministratori è peggiore di nessuna condizione, perché nasconde il problema fino a un incidente.
Fidati solo del proxy che riscrive l’indirizzo
La maggior parte dei siti pubblici sta dietro una CDN o un proxy inverso, quindi l’indirizzo di socket dell’origine appartiene a quel frontend e non al visitatore. Il frontend di solito inoltra l’indirizzo originale in un’intestazione, e quell’intestazione è controllata da un attaccante a meno che l’origine non la accetti solo da proxy noti. Configura esplicitamente l’elenco delle fonti attendibili e rendi l’origine irraggiungibile da ogni altro luogo. Senza quella seconda metà, un attaccante aggira il proxy e invia l’intestazione direttamente.
set_real_ip_from 192.0.2.0/24;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
curl -H 'X-Forwarded-For: 198.51.100.27' https://origin.example.com/admin
Lo stesso ragionamento vale per il protocollo PROXY: è affidabile solo quando il listener è legato a un’interfaccia che il proxy può raggiungere e nient’altro può. Un contesto di indirizzo mancante deve far fallire il controllo protetto invece di cadere in un permesso predefinito. Un’intestazione assente non è prova di un percorso attendibile.
Allega la condizione a ogni operazione protetta
Metti il controllo al confine di autorizzazione che ogni operazione protetta attraversa, non nella pagina che rende il menu. I gruppi di route, il middleware o uno strato di policy funzionano tutti; una copia per handler no, perché l’handler che qualcuno aggiungerà il prossimo trimestre non l’avrà.
user = requireAuthenticatedSession()
requirePermission(user, "admin")
requireVpnEgress(verifiedClientAddress(request))
performProtectedOperation(user, request)
Lo stesso confine deve coprire l’API JSON, le esportazioni, gli URL di download firmati e la stretta di mano WebSocket, poiché nessuno di essi richiede che la pagina HTML venga richiesta prima. Un link di download creato mentre la VPN era attiva resta valido dopo, a meno che l’endpoint di download non ricontrolli. Le sessioni di lunga durata hanno bisogno anch’esse di una regola di rivalutazione, altrimenti una richiesta autenticata diventa un’esenzione permanente. Il rifiuto predefinito è l’unica impostazione che sopravvive al ricambio del personale.
Le cache condivise sono l’altro modo di aggirare il controllo. Una cache che contiene già una risposta protetta può servirla senza contattare l’origine, quindi contrassegna le risposte amministrative come private e tienile fuori dallo spazio di archiviazione condiviso. Se una cache deve servirle, il controllo di accesso deve essere eseguito prima del servizio, il che di solito significa che non può essere affatto una cache condivisa.
Metti alla prova i bypass che hai appena chiuso
Esercita la matrice invece del percorso felice: un amministratore attraverso la VPN, lo stesso amministratore senza, un non amministratore attraverso, e un’intestazione di indirizzo falsificata. Aggiungi una richiesta diretta all’origine e una richiesta con risposta in cache, perché sono quelle due che saltano del tutto il frontend.
curl -sI https://admin.example.com/export.csv
curl -s -H 'X-Forwarded-For: 198.51.100.27' https://origin.example.com/admin
curl -s https://admin.example.com/api/me -o /dev/null -w '%{http_code}\n'
Ogni richiesta dovrebbe restituire lo stato che la politica promette, e il registro dovrebbe annotare quale prova dell’indirizzo è stata usata. Copri IPv6 separatamente, perché un’origine dual-stack può vedere una famiglia di sorgenti diversa da quella che hai testato. Mantieni operative le normali operazioni dei clienti fuori dalla VPN, o la modifica verrà annullata alla prima lamentela di un cliente. Scrivi il risultato atteso per ogni riga prima di eseguirla, così un superamento inatteso sia visibile.
L’indirizzo di uscita prova un percorso di rete consentito, non una persona e non un dispositivo gestito. Tienilo come una condizione accanto all’autenticazione e al permesso, e l’area amministrativa resta stretta senza trasformare l’intero prodotto in un servizio solo VPN. Quella separazione è il progetto, non un suo limite.