ขั้นตอนสำหรับ จำกัด การเชื่อมต่อนอกไปยังคอนเทนเนอร์นักเทียบท่าด้วย iptables?


20

เป้าหมายของฉันคือ จำกัด การเข้าถึงคอนเทนเนอร์ของนักเทียบท่าที่อยู่ IP สาธารณะเพียงไม่กี่แห่ง มีกระบวนการที่เรียบง่ายและทำซ้ำได้เพื่อบรรลุเป้าหมายของฉันหรือไม่ ทำความเข้าใจกับพื้นฐานของ iptables ในขณะที่ใช้ตัวเลือกเริ่มต้นของ Docker ฉันพบว่ามันยากมาก

ฉันต้องการเรียกใช้คอนเทนเนอร์ทำให้เห็นอินเทอร์เน็ตสาธารณะ แต่อนุญาตเฉพาะการเชื่อมต่อจากโฮสต์ที่เลือก ฉันคาดว่าจะตั้งค่านโยบายอินพุตเริ่มต้นของ REJECT แล้วอนุญาตเฉพาะการเชื่อมต่อจากโฮสต์ของฉัน แต่กฎและโซ่ NAT ของ Docker เข้ามาขวางและกฎ INPUT ของฉันจะถูกละเว้น

ใครบางคนสามารถเป็นตัวอย่างของวิธีการบรรลุเป้าหมายของฉันตามสมมติฐานดังต่อไปนี้?

  • โฮสต์ IP สาธารณะ 80.80.80.80 บน eth0
  • โฮสต์ IP ส่วนตัว 192.168.1.10 บน eth1
  • docker run -d -p 3306:3306 mysql
  • บล็อกการเชื่อมต่อทั้งหมดไปยังโฮสต์ / คอนเทนเนอร์ 3306 ยกเว้นจากโฮสต์ 4.4.4.4 และ 8.8.8.8

ฉันยินดีที่จะผูกคอนเทนเนอร์กับที่อยู่ IP เฉพาะที่อยู่ในท้องที่ แต่ต้องการคำแนะนำเกี่ยวกับวิธีการตั้งค่ากฎการส่งต่อ iptables อย่างถูกต้องซึ่งอยู่รอดกระบวนการนักเทียบท่าและโฮสต์รีสตาร์ท

ขอบคุณ!

คำตอบ:


15

ข้อควรพิจารณาสองประการเมื่อทำงานกับกฎไฟร์วอลล์ของนักเทียบท่า:

  1. เพื่อหลีกเลี่ยงกฎของคุณที่ถูกอุดตันโดยนักเทียบท่าใช้DOCKER-USERโซ่
  2. นักเทียบท่าทำการแมปพอร์ตในPREROUTINGสายโซ่ของnatตาราง สิ่งนี้เกิดขึ้นก่อนfilterกฎดังนั้น--destและ--dportจะเห็น IP ภายในและพอร์ตของคอนเทนเนอร์ -m conntrack --ctorigdstportในการเข้าถึงปลายทางเดิมคุณสามารถใช้

ตัวอย่างเช่น:

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

หมายเหตุ: หากไม่ทำ--ctdir ORIGINALเช่นนี้จะเป็นการจับคู่แพ็กเก็ตตอบกลับที่มาจากการเชื่อมต่อจากคอนเทนเนอร์ไปยังพอร์ต 3306 บนเซิร์ฟเวอร์อื่นซึ่งบางครั้งก็ไม่ใช่สิ่งที่คุณต้องการ! คุณไม่จำเป็นต้องใช้สิ่งนี้อย่างเด็ดขาดถ้าอย่างฉันกฎข้อแรกของคุณก็คือ-m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPTเพราะมันจะจัดการกับแพ็คเก็ตตอบกลับทั้งหมด แต่มันก็ยังปลอดภัยกว่าที่จะใช้งาน--ctdir ORIGINALต่อไป


ควรแก้ไขให้รวม --ctdirหรือไม่ ฉันใช้-m conntrack --ctstate NEW --ctorigdstport 3306 --ctdir ORIGINAL
lonix

@Ionix ใช่มันควรจะเป็นแม้ว่าฉันเพิ่งจะคิดออกมาเท่านั้นว่าทำไมมันถึงทำให้ฉันสับสน ฉันได้เพิ่มคำอธิบายเล็กน้อย
SystemParadox

1
หมายเหตุเริ่มต้นDOCKER-USERตารางประกอบด้วยรายการนี้ซึ่งจะทำงานก่อนที่ดังกล่าวข้างต้นถ้าคุณใช้-A DOCKER-USER -j RETURN วิธีการแก้ปัญหาคือการแทรกกฎที่หัวเพื่อย้อนกลับด้วย-A -I
BMitch

@BMitch หรือดียิ่งขึ้นยังเพิ่มกฎทั้งหมดในFILTERSห่วงโซ่ใหม่และ-Iแทรกกฎใหม่ (เช่นที่คุณพูด) เพื่อข้ามไปที่มัน: -I INPUT -j FILTERSและ-I DOCKER-USER -i eth0 -j FILTERS
lonix

@BMitch อย่างไรก็ตามฉันเพิ่งตรวจสอบเซิร์ฟเวอร์ของฉันและกฎการส่งคืนไม่ได้อยู่ที่นั่นบางทีนักเทียบท่ารุ่นล่าสุดจะไม่แทรกอีกต่อไป ความคิดที่ดีที่จะใช้-Iแต่เพียงเพื่อความปลอดภัย
lonix

8

ด้วย Docker v.17.06 จะมีเครือข่าย iptables ใหม่ชื่อ DOCKER-USER กฎนี้ใช้สำหรับกฎที่กำหนดเองของคุณ: https://docs.docker.com/network/iptables/

ซึ่งแตกต่างจากโซ่ DOCKER มันไม่ได้ถูกรีเซ็ตในการสร้าง / ภาชนะเริ่มต้น ดังนั้นคุณสามารถเพิ่มบรรทัดเหล่านี้ใน iptables config / script ของคุณเพื่อจัดเตรียมเซิร์ฟเวอร์แม้กระทั่งก่อนที่จะติดตั้งนักเทียบท่าและเริ่มต้นคอนเทนเนอร์:

-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

ตอนนี้พอร์ตสำหรับ MySQL ถูกบล็อกจากการเข้าถึงภายนอก (eth0) แม้คิดว่านักเทียบท่าเปิดพอร์ตสำหรับโลก (กฎเหล่านี้ถือว่าส่วนต่อประสานภายนอกของคุณคือ eth0)

ในที่สุดคุณจะต้องทำความสะอาด iptables รีสตาร์ทบริการนักเทียบท่าก่อนถ้าคุณทำมันยุ่งเกินไปพยายามล็อคพอร์ตเหมือนที่ฉันทำ


ฉันคิดถึงว่าทำไมตาราง DOCKER-USER นี้จึงแตกต่างจากตารางที่ผู้ใช้เพิ่มอื่น ๆ .. มันไม่มีตัวกรองที่นำไปใช้ล่วงหน้าดังนั้นคุณยังคงต้องระบุชื่ออินเทอร์เฟซด้วยตัวคุณเอง หากคุณสร้าง "MY-CHAIN" และใส่ลงในห่วงโซ่ไปข้างหน้ามันจะมีผลเหมือนกันใช่ไหม?
ColinM

ใช่มันสร้างความแตกต่างเพราะนักเทียบท่าใส่โซ่ DOCKER-USER เข้าไปในห่วงโซ่ FORWARD: -A FORWARD -j DOCKER-USER -A FORWARD -j DOCKER-ISOLATION นั่นเป็นเหตุผลว่าทำไมคำสั่งที่กำหนดเองจะถูกดำเนินการก่อนที่โซ่ DOCKER
ck1

โปรดทราบว่าหากคุณใช้--dportภายใน DOCKER-USER สิ่งนี้จะต้องตรงกับIP ภายในของบริการคอนเทนเนอร์ไม่ใช่พอร์ตที่เปิดเผย สิ่งเหล่านี้มักจะตรงกัน แต่ไม่เสมอไปและสิ่งนี้อาจขัดแย้งกับบริการอื่น ๆ ได้อย่างง่ายดายดังนั้นฉันจึงยังคงยืนยันว่าโซลูชัน DOCKER-USER นี้มีการใช้งานเพียงครึ่งเดียว
ColinM

4

อัปเดต : แม้ว่าจะใช้ได้ในปี 2015 โซลูชันนี้จะไม่เหมาะสมอีกต่อไป

คำตอบน่าจะอยู่ในเอกสารของ Docker ที่https://docs.docker.com/articles/networking/#the-world

กฎการส่งต่อของนักเทียบท่าอนุญาต IP ต้นทางภายนอกทั้งหมดตามค่าเริ่มต้น หากต้องการอนุญาตเฉพาะ IP หรือเครือข่ายเฉพาะที่เข้าถึงคอนเทนเนอร์ให้ใส่กฎที่ไม่ได้ใช้ที่ด้านบนของห่วงโซ่ตัวกรอง DOCKER ตัวอย่างเช่นในการ จำกัด การเข้าถึงจากภายนอกซึ่งเฉพาะ IP ต้นทาง 8.8.8.8 เท่านั้นที่สามารถเข้าถึงคอนเทนเนอร์ได้สามารถเพิ่มกฎต่อไปนี้ได้:iptables -I DOCKER -i ext_if ! -s 8.8.8.8 -j DROP

สิ่งที่ฉันทำคือ:

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

ฉันไม่ได้แตะต้อง--iptablesหรือ--iccตัวเลือก


1
หากคุณทำเช่นiptables -vnL DOCKERนั้นพอร์ตปลายทางคือพอร์ตทั้งหมดภายในคอนเทนเนอร์ ถ้าฉันเข้าใจถูกต้องนั่นหมายความว่ากฎข้างต้นจะมีผลกับพอร์ต3306ภายในคอนเทนเนอร์เท่านั้นนั่นคือถ้าคุณอยู่ใน-p 12345:3306คอนเทนเนอร์ของคุณกฎของคุณจะยังคงเป็นกฎที่จำเป็นในการล็อคการเข้าถึง (เช่น--dport 12345จะไม่ทำงาน) เนื่องจากกฎ ACCEPT ของเชน DOCKER เป็น post-NAT
ซันไซด์

ถูกต้องกฎต้องเกี่ยวข้องกับพอร์ตภายในคอนเทนเนอร์
GGGforce

1
Hum, นั่นเป็นเรื่องที่น่าเกลียดถ้าคุณใช้หลาย ๆ container ที่ใช้, พูด, NGINX ภายในเพื่อทำ reverse proxying (เช่น Zabbix, load balancer ที่กำหนดเองเป็นต้น) เพราะมันจะทำให้คุณต้องรู้ IP ของ container ล่วงหน้า ฉันยังคงค้นหาวิธีแก้ไขปัญหาที่ไม่ต้องการ--iptables=falseเพราะสิ่งนี้ดูเหมือนจะเป็นตัวเลือกที่แย่ที่สุดของพวกเขาทั้งหมด
ซันไซด์

ขอขอบคุณ! คุณได้แก้ไขปัญหาของฉันหลังจากค้นหาหลายชั่วโมง ในที่สุดฉันก็สามารถติดคุก MySQL ได้เพียงแค่ที่อยู่ IP ที่บ้านของฉันโดยไม่เปิดเผยจุดอ่อนไปทั่วโลก
Matt Cavanagh

1
ห่วงโซ่ DOCKER ไม่ควรถูกควบคุมโดยผู้ใช้โดยตรง! ใช้เชน DOCKER-USER สำหรับสิ่งนั้น ตรวจสอบคำตอบที่ยอมรับ
พอล - เซบาสเตียน Manole

3

ปรับปรุง:ในขณะที่คำตอบนี้ยังคงใช้ได้คำตอบโดย @SystemParadox ใช้DOCKER-USERร่วมกับ--ctorigdstportจะดีกว่า

นี่เป็นวิธีแก้ปัญหาที่ยังคงมีอยู่ระหว่างการรีสตาร์ทและช่วยให้คุณมีผลกับพอร์ตที่ถูกเปิดเผยมากกว่าพอร์ตภายใน

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

ฉันได้สร้างอิมเมจ Docker ที่ใช้วิธีนี้เพื่อจัดการ iptables ให้คุณโดยอัตโนมัติโดยใช้ตัวแปรสภาพแวดล้อมหรือแบบไดนามิกกับ etcd (หรือทั้งสองอย่าง):

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.