Puntare un host virtuale Subversion su un indirizzo VPN cambia quale sito risponde a una richiesta, non chi è autorizzato a inviarne una. Il controllo dell’accesso avviene più tardi, secondo le regole della sezione che corrispondeva all’URL, e il nome dell’host non entra mai in quella decisione. Se ServerName è stata l’unica modifica, il repository è esattamente accessibile come ieri. Due domande distinte devono trovare risposta nella configurazione: quale socket ha accettato la connessione e quale identità portava la richiesta.

Che cosa decide davvero ServerName

L’hosting virtuale basato sul nome confronta l’intestazione Host con le voci ServerName e ServerAlias, sceglie un host e lo lascia gestire la richiesta. La connessione stessa era stata accettata prima da un socket Listen che appartiene al server, non all’host. Per questo un nome dall’aria privata non offre alcuna protezione: qualsiasi client che raggiunga il socket può inviare quell’intestazione Host. Il nome è una chiave di instradamento, e le chiavi di instradamento le sceglie chi chiama.

Potete vedere la mappatura senza indovinare. apachectl -S stampa ogni host, la sua porta e i suoi nomi, e segna quale è il predefinito per l’indirizzo.

RequireAll è il contenitore che rende obbligatori entrambi i controlli

Apache 2.4 avvolge le direttive Require di una singola sezione in un RequireAny implicito, quindi due direttive nude significano che una delle due basta. Aggiungete il contenitore e il significato si inverte:

<Location /svn/repo>
    AuthType Basic
    AuthName "Subversion"
    AuthBasicProvider file
    AuthUserFile /etc/apache2/svn.htpasswd
    <RequireAll>
        Require ip 10.8.0.0/24
        Require valid-user
    </RequireAll>
</Location>

RequireAll richiede entrambe le condizioni: un indirizzo di origine nell’intervallo del tunnel e un utente che il repository accetti. È questo che dovrebbe significare solo-VPN per un URL Subversion. La documentazione di mod_authz_core descrive le regole del contenitore. Mantenete il blocco nel Location esistente del repository e lasciate al loro posto le direttive DAV che già funzionano, perché questo frammento non sostituisce né AuthType né il file delle password né l’autorizzazione a livello di percorso.

Il test di origine viene da mod_authz_host, quindi confermate che sia caricato prima di eseguire il debug di qualsiasi altra cosa: apachectl -M | grep authz_host.

L’indirizzo che Apache valuta è l’indirizzo del socket

Require ip confronta con l’indirizzo che Apache ha registrato per la connessione, e non è sempre quello che il client crede di avere.

Quando la VPN instrada i suoi indirizzi senza traduzione, Apache vede l’indirizzo del tunnel e 10.8.0.0/24 corrisponde. Quando un gateway traduce, Apache vede il gateway e la regola non corrisponde silenziosamente a nulla; lo stesso accade dietro un bilanciatore che termina TLS davanti all’host. Per quel caso esiste mod_remoteip. RemoteIPHeader X-Forwarded-For insieme a RemoteIPTrustedProxy per il bilanciatore sostituisce l’indirizzo registrato, e solo allora l’intervallo ha qualcosa con cui confrontarsi.

Fidarsi di un’intestazione di inoltro arbitraria è peggio che non avere alcuna regola. Qualsiasi client può inviare X-Forwarded-For contenente 10.8.0.9 e soddisfare un controllo di intervallo che non è mai stato ancorato a un salto fidato.

Sovrapponete l’ascoltatore e la politica del repository

Il binding è un controllo separato e spesso il più semplice. Listen 10.8.0.1:443 decide quali socket esistono, e una richiesta non può scegliere un host su un socket che non è mai stato aperto. Il prezzo è l’ordine di avvio: se l’interfaccia del tunnel è inattiva quando Apache parte, il binding fallisce e il servizio non sale. Abbinatelo all’ordine delle unità, oppure vincolate a un indirizzo che esiste indipendentemente dal tunnel.

I permessi di percorso restano in AuthzSVNAccessFile. Un account valido per il sito non è la stessa concessione dell’accesso in scrittura a un percorso di progetto, e la regola di rete non lo cambia.

Fate attenzione alla fusione delle sezioni quando il blocco si trova in un file incluso. Apache fonde i contenitori di autorizzazione attraverso sezioni annidate e la sezione corrispondente più specifica può sostituire il requisito invece di aggiungersi, quindi una regola su /svn potrebbe non raggiungere /svn/repo/trunk quando una sezione più stretta definisce la propria.

Dimostrate entrambi i rifiuti prima di dichiarare il lavoro finito

Provate quattro richieste, non una. Dentro la VPN con un account valido dovreste ottenere l’elenco del repository. Dentro la VPN con una password errata dovreste ottenere 401 invece di un elenco. Da fuori la VPN con credenziali valide dovreste ottenere 403, e la stessa richiesta pubblica verso un nome host alternativo deve fallire allo stesso modo.

Un comando per caso basta: curl -sI -u user:pass https://svn.example.com/svn/repo/, poi leggete la riga di stato. Ripetete anche il caso funzionante con il client reale, perché svn info esercita i metodi DAV che useranno i commit e gli aggiornamenti.

Se uno dei quattro risultati contraddice la politica prevista, la configurazione non è finita, e rinominare l’host non cambierà nulla.

Articoli correlati