I passaggi per limitare le connessioni esterne al contenitore docker con iptables?


20

Il mio obiettivo è limitare l'accesso ai contenitori docker a pochi indirizzi IP pubblici. Esiste un processo semplice e ripetibile per raggiungere il mio obiettivo? Comprendere solo le basi di iptables mentre si utilizzano le opzioni predefinite di Docker, lo trovo molto difficile.

Vorrei eseguire un container, renderlo visibile a Internet pubblico, ma consentire solo connessioni da host selezionati. Mi aspetterei di impostare una politica INPUT predefinita di REJECT e quindi consentire solo le connessioni dai miei host. Ma le regole e le catene NAT di Docker si frappongono e le mie regole INPUT vengono ignorate.

Qualcuno può fornire un esempio di come raggiungere il mio obiettivo alla luce dei seguenti presupposti?

  • Host IP pubblico 80.80.80.80 su eth0
  • Host IP privato 192.168.1.10 su eth1
  • docker run -d -p 3306:3306 mysql
  • Blocca tutte le connessioni all'host / container 3306 tranne che dagli host 4.4.4.4 e 8.8.8.8

Sono felice di associare il contenitore solo all'indirizzo IP locale, ma avrei bisogno di istruzioni su come impostare correttamente le regole di inoltro di iptables che sopravvivono al processo docker e al riavvio dell'host.

Grazie!

Risposte:


15

Due cose da tenere a mente quando si lavora con le regole del firewall della docker:

  1. Per evitare che le regole vengano bloccate dalla finestra mobile, utilizzare la DOCKER-USERcatena
  2. Docker esegue il mapping delle porte nella PREROUTINGcatena della nattabella. Questo avviene prima che le filterregole, così --deste --dportvedrà l'IP interno e la porta del contenitore. Per accedere alla destinazione originale, è possibile utilizzare -m conntrack --ctorigdstport.

Per esempio:

iptables -A DOCKER-USER -i eth0 -s 8.8.8.8 -p tcp -m conntrack --ctorigdstport 3306 --ctdir ORIGINAL -j ACCEPT
iptables -A DOCKER-USER -i eth0 -s 4.4.4.4 -p tcp -m conntrack --ctorigdstport 3306 --ctdir ORIGINAL -j ACCEPT
iptables -A DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 3306 --ctdir ORIGINAL -j DROP

NOTA: Senza --ctdir ORIGINAL, questo corrisponderebbe anche ai pacchetti di risposta che ritornano per una connessione dal contenitore alla porta 3306 su qualche altro server, che quasi sicuramente non è quello che vuoi! Non è strettamente necessario se come me è la tua prima regola -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT, poiché si occuperà di tutti i pacchetti di risposta, ma sarebbe comunque più sicuro utilizzarlo --ctdir ORIGINALcomunque.


Questo dovrebbe essere modificato per includere il --ctdir ? Uso-m conntrack --ctstate NEW --ctorigdstport 3306 --ctdir ORIGINAL
lonix il

@Ionix, sì, dovrebbe, anche se ho appena capito perché mi ha confuso. Ho aggiunto un po 'di spiegazione.
SystemParadox,

1
Nota la DOCKER-USERtabella predefinita contiene la voce: -A DOCKER-USER -j RETURNche verrà eseguita prima di quanto sopra se si utilizza -A. Una soluzione è quella di inserire le regole in testa in ordine inverso con -I.
BMitch,

@BMitch O ancora meglio , aggiungi tutte le regole in una nuova FILTERScatena e -Iinserisci nuove regole (come hai detto tu), per saltarci sopra: -I INPUT -j FILTERSe-I DOCKER-USER -i eth0 -j FILTERS
lonix,

@BMitch Tuttavia, ho appena controllato il mio server e la regola di restituzione non è presente, forse l'ultima versione della finestra mobile non la inserisce più? Buona idea da usare -Iperò, solo per sicurezza.
Lonix,

8

Con Docker v.17.06 c'è una nuova catena iptables chiamata DOCKER-USER. Questo è per le tue regole personalizzate: https://docs.docker.com/network/iptables/

A differenza della catena DOCKER, non viene ripristinato sulla costruzione / avvio di container. Quindi potresti aggiungere queste righe al tuo iptables config / script per eseguire il provisioning del server anche prima di installare la finestra mobile e avviare i contenitori:

-N DOCKER
-N DOCKER-ISOLATION
-N DOCKER-USER
-A DOCKER-ISOLATION -j RETURN
-A DOCKER-USER -i eth0 -p tcp -m tcp --dport 3306 -j DROP
-A DOCKER-USER -j RETURN

Ora la porta per MySQL è bloccata dall'accesso esterno (eth0) anche se la finestra mobile apre la porta al mondo. (Queste regole presuppongono che l'interfaccia esterna sia eth0.)

Alla fine, dovrai ripulire iptables prima di riavviare il servizio docker, se hai fatto troppo casino cercando di bloccare la porta come ho fatto io.


Mi manca il motivo per cui questa tabella DOCKER-USER è diversa rispetto a qualsiasi altra tabella aggiunta dall'utente .. Non ha alcun filtro pre-applicato ad essa, quindi è ancora necessario specificare i nomi dell'interfaccia da soli. Se si crea una "MY-CHAIN" e la si inserisce nella catena FORWARD avrà lo stesso risultato, no?
ColinM,

Sì, fa la differenza, perché Docker inserisce la catena DOCKER-USER nella catena FORWARD: -A FORWARD -j DOCKER-USER -A FORWARD -j DOCKER-ISOLATION ecco perché le istruzioni personalizzate vengono eseguite prima della catena DOCKER.
ck1

Se si utilizza --dportDOCKER-USER, questo deve corrispondere all'IP interno del servizio contenitore, non alla porta esposta. Questi spesso coincidono, ma non sempre, e questo potrebbe facilmente entrare in conflitto con altri servizi, quindi continuo a sostenere che questa soluzione DOCKER-USER è quasi cotta.
ColinM,

4

AGGIORNAMENTO : Sebbene valida nel 2015, questa soluzione non è più il modo giusto per farlo.

La risposta sembra essere nella documentazione di Docker su https://docs.docker.com/articles/networking/#the-world

Le regole di inoltro di Docker consentono per impostazione predefinita tutti gli IP di origine esterna. Per consentire solo a un IP o una rete specifici di accedere ai contenitori, inserire una regola negata nella parte superiore della catena di filtri DOCKER. Ad esempio, per limitare l'accesso esterno in modo che solo l'IP 8.8.8.8 di origine possa accedere ai contenitori, è possibile aggiungere la seguente regola:iptables -I DOCKER -i ext_if ! -s 8.8.8.8 -j DROP

Quello che ho finito per fare è stato:

iptables -I DOCKER -i eth0 -s 8.8.8.8 -p tcp --dport 3306 -j ACCEPT
iptables -I DOCKER -i eth0 -s 4.4.4.4 -p tcp --dport 3306 -j ACCEPT
iptables -I DOCKER 3 -i eth0 -p tcp --dport 3306 -j DROP

Non ho toccato le opzioni --iptableso --icc.


1
In tal caso iptables -vnL DOCKER, le porte di destinazione sono tutte le porte all'interno del contenitore. Se avessi ragione, ciò significherebbe che le regole di cui sopra influirebbero solo sulla porta 3306all'interno del contenitore - ovvero, se tu fossi nel -p 12345:3306tuo contenitore, la tua regola sarebbe comunque quella richiesta per bloccare l'accesso (cioè --dport 12345non funzionerebbe) , poiché le regole ACCEPT della catena DOCKER sono post NAT.
lato sole

Esatto, le regole devono riguardare le porte all'interno dei contenitori.
GGGforce,

1
Hum, è un po 'brutto se ti capita di eseguire più container che usano, ad esempio, un NGINX interno per eseguire il proxy inverso (ad esempio Zabbix, un bilanciamento del carico personalizzato, ecc.) Perché richiederebbe di conoscere in anticipo l'IP del contenitore. Sto ancora cercando una soluzione per quel problema che non richiede --iptables=false, perché questa sembra essere la scelta peggiore di tutte.
lato sole

Grazie! Hai risolto il mio problema dopo molte ore di ricerche. Ora sono finalmente in grado di imprigionare MySQL solo al mio indirizzo IP di casa senza esporre il ventre molle al mondo intero.
Matt Cavanagh,

1
La catena DOCKER non dovrebbe essere manipolata direttamente dall'utente! Usa la catena DOCKER-USER per questo. Controlla la risposta accettata.
Paul-Sebastian Manole,

3

AGGIORNAMENTO: Mentre questa risposta è ancora valida, la risposta di @SystemParadox che utilizza DOCKER-USERin combinazione con --ctorigdstportè migliore.

Ecco una soluzione che persiste bene tra i riavvii e consente di influire sulla porta esposta piuttosto che sulla porta interna .

iptables -t mangle -N DOCKER-mysql iptables -t mangle -A DOCKER-mysql -s 22.33.44.144/32 -j RETURN iptables -t mangle -A DOCKER-mysql -s 22.33.44.233/32 -j RETURN iptables -t mangle -A DOCKER-mysql -j DROP iptables -t mangle -A PREROUTING -i eth0 -p tcp -m tcp --dport 3306 -j DOCKER-mysql

Ho creato un'immagine Docker che utilizza questo metodo per gestire automaticamente iptables per te, usando le variabili di ambiente o dinamicamente con etcd (o entrambi):

https://hub.docker.com/r/colinmollenhour/confd-firewall/

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.