Recent, am început să migrez un server web cu mai multe aplicații pe un server nou și le-am grupat pe fiecare
aplicație într-un container docker. Configurația mea actuală constă în nginx pentru proxy invers și servere de baze de date
rulează pe serverul însuși și toate aplicațiile web care rulează în propriile lor containere docker.
Acum încerc să securizez serverul web folosind iptables, așa cum făceam cu mulți ani înainte. eu
trebuie sa indeplineasca urmatoarele conditii:
- Paravanul de protecție normal pentru serviciile non-docker ar trebui să funcționeze în continuare (politicile implicite să renunțe atât pentru intrare, cât și pentru ieșire, doar porturile denumite în mod explicit accesibile)
- containerele (cu o singură excepție - vezi mai jos) nu trebuie să fie accesibile din lume
- containerele trebuie să poată accesa gazdă folosind „docker.host.internal” pentru a accesa serverele de baze de date
- containerele trebuie să poată accesa serviciile altor containere folosind numele lor de domenii publice (proxiate).
- un singur container ar trebui să aibă portul 22 accesibil direct din lume (git)
Până acum, am reușit să satisfac doar primele trei cerințe. Configurarea mea simplă pentru iptables, din care am adoptat-o
mediul anterior, non-docker, arată cam așa:
# politici implicite de eliminat
-P CĂDERARE INTRARE
-P PĂDURA ÎNTÂMPRE
-P CADEREA IESIRI
# permite navigarea pe internet, NTP, DNS
-A IEȘIRE -p tcp -m tcp --dport 80 -j ACCEPT
-A IEȘIRE -p tcp -m tcp --dport 443 -j ACCEPT
-A INTRARE -p tcp -m tcp --sport 80 -m stare --stare RELATED,ESTABLISHED -j ACCEPT
-A INTRARE -p tcp -m tcp --sport 443 -m stare --stare RELATED,ESTABLISHED -j ACCEPT
-A INTRARE -p udp -m udp --sport 53 -j ACCEPT
-A INTRARE -p tcp -m tcp --sport 53 -j ACCEPT
-A IEȘIRE -p udp -m udp --dport 53 -j ACCEPT
-A IEȘIRE -p tcp -m tcp --dport 53 -j ACCEPT
-A INTRARE -p udp -m udp --dport 123 -j ACCEPT
-A IEȘIRE -p udp -m udp --sport 123 -j ACCEPT
# permit servicii - server web, server SSH pe portul 16 (știu că nu este standard, dar m-am obișnuit cu asta) și SSH pe portul 22 pentru accesarea git
-A INTRARE -p tcp -m tcp --dport 22 -j ACCEPT
-A INTRARE -p tcp -m tcp --dport 16 -j ACCEPT
-A INTRARE -p tcp -m tcp --dport 80 -j ACCEPT
-A INTRARE -p tcp -m tcp --dport 443 -j ACCEPT
-A IEȘIRE -p tcp -m tcp --sport 22 -m stare --stare RELATED,STABLISHED -j ACCEPT
-A IEȘIRE -p tcp -m tcp --sport 16 -m stare --stare RELATED,ESTABLISHED -j ACCEPT
-A IEȘIRE -p tcp -m tcp --sport 80 -m stare --stare RELATED,ESTABLISHED -j ACCEPT
-A IEȘIRE -p tcp -m tcp --sport 443 -m stare --stare RELATED,STABLISHED -j ACCEPT
# permite toată comunicarea locală
-A INTRARE -i lo -j ACCEPT
-A IEȘIRE -o lo -j ACCEPT
# permite ping
-A FORWARD -p icmp -j ACCEPT
-A INTRARE -p icmp -j ACCEPT
-A IEȘIRE -p icmp -j ACCEPT
Nu susțin că acesta este cel mai bun mod de a face lucrurile, dar ar trebui să fie destul de sigur și a funcționat pentru mine de ani de zile.
Acum trec la chestiile pe care nu le înțeleg prea bine. Dacă aplic acest lucru la noua mea configurație docker, se întrerupe accesul la gazdă folosind aliasul „docker.host.internal”. Pentru a permite asta,
Obisnuiam:
iptables -A INTRARE -i br-+ -j ACCEPT
iptables -A IEȘIRE -o br-+ -j ACCEPT
Am mai văzut -i docker0
fiind folosit, dar nu a funcționat pentru mine, pentru că fiecare container docker are al lui br-*
interfață din configurația mea pe care o folosește pentru a accesa gazda. Aceasta a funcționat,
Până acum, bine.
Acum am observat că toate porturile publicate de docker (folosesc 80XX pentru servicii web și apoi domeniile corecte de proxy către acele porturi prin proxy nginx) sunt vizibile din lume.
Cu siguranță nu vreau asta, așa că după câteva căutări, am adăugat asta:
iptables -I DOCKER-USER -i venet0 -j DROP
(venet0
este numele interfeței mele de rețea, în loc de eth0
)
Acest lucru a funcționat bine, dar au încălcat condițiile 4 și 5 - containerele nu se mai pot accesa unul pe celălalt pe adresa lor de domeniu public și nu mă pot conecta la serverul git prin portul 22
(la care mă așteptam).
Prin comunicare pe adresa de domeniu public, mă refer la următoarele:
Am un server CI de dronă care rulează într-un singur container, expunând portul 8001
către gazdă. Apoi am un proxy nginx care transmite adresa https://ci.example.com
spre acest port.
Același lucru este valabil și pentru containerul meu Gitea, port expun 8002
și disponibil pe https://git.example.com
.
Drona trebuie să se autentifice prin OAuth cu serverul Gitea. Pentru a face acest lucru, trebuie să îl poată accesa prin intermediul https://git.example.com:443
adresa, nu dockerul intern
http://gitea_container_name:8002
. Am mai multe alte cazuri de utilizare în care acest lucru trebuie să fie posibil, dar acesta explică cel mai bine.
Acum am petrecut multe ore încercând să fac asta să funcționeze, dar fără succes - dacă reușesc să lucrez, alte condiții se întrerup și invers. Unele dintre lucrurile pe care le-am încercat deja (majoritatea provin de la diverse alte întrebări de aici):
# a încercat să activeze un port specific pentru containerul gitea, fără efect
iptables -I DOCKER-USER -i venet0 -p tcp --sport 8002 -j ACCEPT
# a încercat această sugestie, fără efect
iptables -I DOCKER-USER -i venet0 -p tcp -m conntrack --ctorigdstport 443 --ctdir ORIGINAL -j ACCEPT
# fără efect, dar am bănuit că acest lucru nu va funcționa, deoarece nu cred că 443 este ceea ce vine la acest lanț
iptables -I DOCKER-USER -i venet0 -p tcp --dport 443 -j ACCEPT
# fara efect
iptables -A INPUT -i docker0 -j ACCEPT
Este posibilă o astfel de instalare? Poate cineva mai versat în lumea docker și firewall să mă îndrume în direcția corectă?
Configurația mea:
- Debian 11
- iptables v1.8.7 (nf_tables)
- Docker 20.10.5
Multumesc mult anticipat!