Iptables: จับคู่ทราฟฟิกขาออกกับ conntrack และเจ้าของ ทำงานร่วมกับหยดแปลก ๆ


11

ในสคริปต์ iptables ของฉันฉันได้ทดลองเขียนกฎที่ละเอียดที่สุดเท่าที่จะทำได้ ฉัน จำกัด ผู้ใช้ที่ได้รับอนุญาตให้ใช้บริการใดส่วนหนึ่งเพื่อความปลอดภัยและเป็นแบบฝึกหัดการเรียนรู้

การใช้ iptables v1.4.16.2 บน Debian 6.0.6 ที่รันเคอร์เนล 3.6.2

อย่างไรก็ตามฉันมีปัญหาฉันยังไม่ค่อยเข้าใจ

พอร์ตขาออกสำหรับผู้ใช้ทั้งหมด

มันใช้งานได้ดีอย่างสมบูรณ์แบบ ฉันไม่มีกฎการติดตามสถานะทั่วไป

## พอร์ตขาออก 81
$ IPTABLES - เอาท์พุท -p tcp - พอร์ต 81 -m conntrack - สถานะใหม่, ก่อตั้งขึ้น -j ACCEPT
$ IPTABLES - อินพุต -p tcp --sport 81 -s $ MYIP -m conntrack - สถานะของรัฐที่จัดตั้งขึ้น -j ACCEPT

พอร์ตขาออกพร้อมการจับคู่ผู้ใช้

## พอร์ตขาออก 80 สำหรับผู้ใช้บัญชี
$ IPTABLES -A เอาท์พุท - จับคู่เจ้าของ - ผู้ใช้ useraccount -p tcp - พอร์ต 80 -m conntrack - สถานะใหม่, สร้างแล้ว --sport 1024: 65535 -j ยอมรับ
$ IPTABLES - อินพุต -p tcp --sport 80 --dport 1024: 65535 -d $ MYIP -m conntrack - สถานะของรัฐถูกสร้าง -j ยอมรับ

วิธีนี้อนุญาตให้พอร์ต 80 ใช้สำหรับบัญชี "useraccount" เท่านั้น แต่กฎเช่นนี้สำหรับปริมาณการใช้ TCP มีปัญหา

## บันทึกขาออกเริ่มต้น + กฎการบล็อก
$ IPTABLES -A เอาท์พุท -j LOG --log-prefix "BAD OUTGOING" --log-ip-options --log-tcp-options --log-uid
$ IPTABLES -A เอาท์พุท -j DROP

ปัญหา

การทำงานข้างต้นผู้ใช้ "useraccount" สามารถรับไฟล์ได้อย่างสมบูรณ์แบบ ไม่มีผู้ใช้รายอื่นในระบบที่สามารถทำการเชื่อมต่อขาออกไปยังพอร์ต 80

useraccount @ host: $ wget http://cachefly.cachefly.net/10mb.test

แต่ wget ด้านบนทำให้ x7 ลดรายการใน syslog ของฉัน:

18 ตุลาคม 02:00:35 xxxx เคอร์เนล: ไม่ดีเข้า = ออก = eth0 SRC = xx.xx.xx.xx DST = 205.234.175.175 LEN = 40 TOS = 0x00 PREC = 0x00 TTL = 64 ID = 12170 DF PROTO = TCP SPT = 37792 DPT = 80 SEQ = 164520678 ACK = 3997126942 WINDOW = 979 RES = 0x00 ACK URGP = 0  

ฉันไม่ได้รับหยดเหล่านี้สำหรับกฎที่คล้ายกันกับปริมาณการใช้ UDP ฉันมีกฎอยู่แล้วซึ่ง จำกัด ว่าผู้ใช้คนใดที่สามารถร้องขอ DNS ได้

แพ็กเก็ต ACK ขาออกที่หลุดดูเหมือนจะมาจากบัญชีรูท (URGP = 0) ซึ่งฉันไม่เข้าใจ แม้ตอนที่ฉันสลับ useraccount สำหรับรูท

ฉันเชื่อว่าแพ็กเก็ต ACK จัดอยู่ในประเภทใหม่เนื่องจาก conntrack เริ่มติดตามการเชื่อมต่อหลังจากขั้นตอนที่ 3 ของการจับมือ 3 ทาง แต่เหตุใดจึงมีการลดลง

หยดเหล่านี้สามารถละเว้นได้อย่างปลอดภัยหรือไม่

แก้ไข

ดังนั้นฉันมักจะเห็นกฎเช่นนี้ซึ่งทำงานได้ดีสำหรับฉัน:

$ IPTABLES - เอาท์พุท -s $ MYIP -p tcp -m tcp --dport 80 -m state - รัฐใหม่, ก่อตั้งแล้ว -j ACCEPT
$ IPTABLES - อินพุต -p tcp -m tcp --sport 80 -d $ MYIP-state รัฐ - รัฐที่ตั้งไว้ -j ACCEPT

ฉันสลับ "-m state - รัฐ" สำหรับ "-m conntrack --ctstate" เนื่องจากการจับคู่ของรัฐล้าสมัยไปแล้ว

เป็นวิธีปฏิบัติที่ดีที่สุดหรือไม่ที่จะมีกฎการติดตามสถานะทั่วไป? กฎดังกล่าวไม่ถือว่าถูกต้องหรือไม่

เพื่อควบคุมการเชื่อมต่อผู้ใช้ขาออกอย่างแน่นหนาจะดีกว่านี้ไหม

$ IPTABLES - อินพุต -m conntrack - สถานะที่ตั้งไว้ -j ACCEPT
$ IPTABLES - เอาท์พุท -m conntrack - สถานะที่ได้รับการจัดตั้ง -j ACCEPT

$ IPTABLES -A เอาท์พุท -p tcp --dport 80 -s $ SERVER_IP_TUNNEL -m conntrack - สถานะใหม่ - เจ้าของใหม่ - เจ้าของผู้ใช้ - บัญชีผู้ใช้ -j ACCEPT

$ IPTABLES -A เอาท์พุท -p tcp --dport 80 -s $ SERVER_IP_TUNNEL -m conntrack - สถานะใหม่ - เจ้าของใหม่ - เจ้าของอื่น ๆ บัญชี -j ACCEPT

คุณช่วยกรุณาโพสต์กฎทั้งหมดรวมถึงห่วงโซ่ FORWARD และตาราง nat ถ้าโฮสต์นี้ยังกำหนดเส้นทางการรับส่งข้อมูลจาก LAN
Serge

โฮสต์นี้ไม่ได้กำหนดเส้นทางใด ๆ ทราฟฟิกมาจากเครื่องด้วยกฎเหล่านี้ ฉันโพสต์กฎที่เกี่ยวข้องเพื่อรับส่งข้อมูลเฉพาะเท่านั้น
arcX

คำตอบ:


16

เพื่อตัดสั้นเรื่องยาว ACK ที่ถูกส่งเมื่อซ็อกเก็ตไม่ได้เป็นของใคร แทนที่จะปล่อยให้แพ็คเก็ตที่เกี่ยวข้องกับซ็อกเก็ตที่เป็นผู้ใช้ให้แพ็คเก็ตที่เกี่ยวข้องกับการเชื่อมต่อที่ถูกริเริ่มโดยซ็อกเก็ตจากผู้ใช้เป็นxx

เรื่องราวอีกต่อไป

เพื่อให้เข้าใจถึงปัญหาจะช่วยให้เข้าใจวิธีการwgetและคำร้องขอ HTTP ทำงานโดยทั่วไป

ใน

wget http://cachefly.cachefly.net/10mb.test

wgetสร้างการเชื่อมต่อ TCP ไปที่cachefly.cachefly.netและเมื่อสร้างขึ้นแล้วส่งคำขอในโปรโตคอล HTTP ที่ระบุว่า: "กรุณาส่งเนื้อหาของ/10mb.test( GET /10mb.test HTTP/1.1) และโดยวิธีการที่คุณสามารถโปรดไม่ปิดการเชื่อมต่อหลังจากที่คุณทำ ( Connection: Keep-alive) เหตุผล มันเป็นเช่นนั้นเพราะในกรณีที่เซิร์ฟเวอร์ตอบกลับด้วยการเปลี่ยนเส้นทางสำหรับ URL บนที่อยู่ IP เดียวกันก็สามารถนำการเชื่อมต่อกลับมาใช้ใหม่ได้

ตอนนี้เซิร์ฟเวอร์สามารถตอบกลับด้วย "นี่คือข้อมูลที่คุณขอมาระวังว่ามันมีขนาดใหญ่ 10MB ( Content-Length: 10485760) และใช่ตกลงฉันจะเปิดการเชื่อมต่อไว้" หรือถ้าไม่รู้ขนาดของข้อมูล "นี่คือข้อมูลขอโทษที่ฉันไม่สามารถเปิดการเชื่อมต่อได้ แต่ฉันจะบอกเมื่อคุณสามารถหยุดดาวน์โหลดข้อมูลได้โดยปิดการเชื่อมต่อที่สิ้นสุด"

ใน URL ด้านบนเราอยู่ในกรณีแรก

ดังนั้นทันทีที่wgetได้รับส่วนหัวสำหรับการตอบสนองจะรู้ว่างานเสร็จเมื่อได้ดาวน์โหลดข้อมูล 10MB แล้ว

โดยทั่วไปสิ่งที่wgetอ่านข้อมูลจนกว่าจะได้รับและออก 10MB แต่ ณ จุดนั้นมีสิ่งที่ต้องทำอีกมาก แล้วเซิร์ฟเวอร์ล่ะ มีคนบอกว่าให้เปิดการเชื่อมต่อ

ก่อนที่จะออกให้wgetปิด (การcloseเรียกของระบบ) ตัวอธิบายไฟล์สำหรับซ็อกเก็ต เมื่อcloseระบบเสร็จสิ้นการรับทราบข้อมูลที่ส่งโดยเซิร์ฟเวอร์และส่งการFINพูดว่า: "ฉันจะไม่ส่งข้อมูลอีกต่อไป" ณ จุดนั้นcloseผลตอบแทนและwgetออก ไม่มีซ็อกเก็ตที่เชื่อมโยงกับการเชื่อมต่อ TCP อีกต่อไป (อย่างน้อยก็ไม่มีใครเป็นเจ้าของโดยผู้ใช้ใด ๆ ) อย่างไรก็ตามมันยังไม่เสร็จ เมื่อได้รับFINแล้วเซิร์ฟเวอร์ HTTP จะเห็นจุดสิ้นสุดไฟล์เมื่ออ่านคำขอถัดไปจากลูกค้า ใน HTTP นั่นหมายถึง "ไม่ขออีกฉันจะปิดท้าย" ดังนั้นมันจึงส่ง FIN ของมันเช่นกันเพื่อพูดว่า "ฉันจะไม่ส่งอะไรเลยการเชื่อมต่อนั้นกำลังจะหายไป"

เมื่อได้รับ FIN นั้นไคลเอ็นต์จะส่ง "ACK" แต่ ณ จุดwgetนั้นหายไปนานดังนั้น ACK จึงไม่ได้มาจากผู้ใช้คนใดเลย นี่คือเหตุผลที่ไฟร์วอลล์ของคุณถูกบล็อก เนื่องจากเซิร์ฟเวอร์ไม่ได้รับ ACK มันจะส่ง FIN ครั้งแล้วครั้งเล่าจนกว่าจะถึงเวลานั้นและคุณจะเห็น ACK ที่ลดลงมากขึ้น นั่นก็หมายความว่าการทำให้ ACK เหล่านั้นลดลงคุณไม่จำเป็นต้องใช้ทรัพยากรของเซิร์ฟเวอร์ (ซึ่งจำเป็นต้องบำรุงรักษาซ็อกเก็ตในสถานะ LAST-ACK) เป็นระยะเวลาพอสมควร

พฤติกรรมจะแตกต่างกันหากลูกค้าไม่ได้ร้องขอ "Keep-alive" หรือเซิร์ฟเวอร์ไม่ได้ตอบด้วย "Keep-alive"

ดังที่ได้กล่าวไปแล้วถ้าคุณใช้ตัวติดตามการเชื่อมต่อสิ่งที่คุณต้องการทำคือให้ทุก ๆ แพ็กเก็ตในสถานะ ESTABLISHED และ RELATED ผ่านและกังวลเฉพาะNEWแพ็กเก็ตเท่านั้น

หากคุณอนุญาตให้ใช้NEWแพ็คเก็ตจากผู้ใช้xแต่ไม่ใช่แพ็คเก็ตจากผู้ใช้yแพ็กเก็ตอื่น ๆ สำหรับการเชื่อมต่อที่สร้างขึ้นโดยผู้ใช้xจะผ่านและเนื่องจากไม่สามารถสร้างการเชื่อมต่อโดยผู้ใช้y(เนื่องจากเรากำลังปิดกั้นNEWแพ็กเก็ต จะไม่มีแพ็กเก็ตใด ๆ สำหรับyการเชื่อมต่อผู้ใช้ที่ต้องผ่าน


3

สิ่งนี้อนุญาตให้พอร์ต 80 ใช้สำหรับบัญชี "useraccount" เท่านั้น

- อย่างน้อยกฎที่คุณแสดงไม่ได้หมายความถึงเรื่องนี้จริงๆ

นอกจากนี้ยังมีห้องให้คำแนะนำ - อย่าทำการตรวจสอบโดยผู้ใช้บนสตรีม ESTABLISHED เพียงทำการตรวจสอบใหม่ ฉันยังไม่เห็นจุดหนึ่งในการตรวจสอบพอร์ตต้นทางเมื่อทำการตรวจสอบขาเข้าที่สร้างขึ้นสิ่งที่แตกต่างกันคือพอร์ตมันอยู่ในสถานะ ESTABLISHED จาก PoV ของ conntrack แล้ว ไฟร์วอลล์ควรเรียบง่ายที่สุดเท่าที่จะเป็นไปได้ แต่มีประสิทธิภาพดังนั้นวิธีการของ Occamจึงเหมาะสมที่สุด


1
ขอบคุณสำหรับคำแนะนำ. โดยปกติแล้วฉันทุกคนเพื่อความเรียบง่าย แต่นั่นไม่ใช่ประเด็นของแบบฝึกหัดนี้
arcX

1
@arcX แบบฝึกหัดนี้อาจล้มเหลวเนื่องจากซับซ้อนเกินไปและไม่สอดคล้องกัน - IOW พฤติกรรมที่คุณเห็นไม่ได้เกิดจาก netfilter internals (บั๊ก, นิสัยใจคอ) แต่เป็นไปตามที่คุณใช้ พยายามทำให้ชุดกฎง่ายขึ้นก่อน ...
poige
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.