กำหนดค่า nft เพื่อทำให้เซิร์ฟเวอร์ NATed FTP ในเครื่องพร้อมใช้งานแบบสาธารณะ

Aug 18 2020

ทุกอย่างจะอยู่ในเครือข่ายแยกส่วนความปลอดภัยไม่ใช่ปัญหา
eth0 เชื่อมต่อกับเครือข่าย "สาธารณะ" ที่อยู่ที่กำหนดโดย DHCP
eth1 เชื่อมต่อกับเซิร์ฟเวอร์ "เครือข่ายส่วนตัว" ที่ให้ ssh, telnet, "others" และ ftp
เซิร์ฟเวอร์นี้จะมี IP คงที่ (192.168.1.2) ในตัวอย่างนี้

เกตเวย์กำลังเรียกใช้เดเบียนบัสเตอร์และเคอร์เนลลินุกซ์ 4.19.94

nft ใช้กับ NAT
นี่คือการกำหนดค่า nft "เกตเวย์" ของฉันในตอนนี้:

table ip my_nat {
    chain my_prerouting { type nat hook prerouting priority 0;
    tcp dport { 2222 } dnat to :22 # 2222 backdoor for ssh to the gateway
    tcp dport { 1-1023 } dnat to 192.168.1.2
  }
  chain my_postrouting {
    type nat hook postrouting priority 100;
    ip daddr 192.168.1.2  masquerade
  }
}

ฉันต้องเพิ่มอะไรเพื่อให้ FTP ทำงานได้

คำตอบ

1 A.B Aug 20 2020 at 19:17

TL; ดร

# nft add ct helper ip my_nat ftp-incoming '{ type "ftp" protocol tcp; }'
# nft add chain ip my_nat my_helpers '{ type filter hook prerouting priority 10; }'
# nft add rule ip my_nat my_helpers iif eth0 ip daddr 192.168.1.2 tcp dport 21 ct helper set ftp-incoming
# modprobe nf_nat_ftp

พร้อมรายละเอียดเพิ่มเติมด้านล่าง ...

โมดูลตัวช่วยโปรโตคอลสำหรับโปรโตคอลที่มีปัญหา

FTP เป็นโปรโตคอลแบบเก่าและไม่เป็นมิตรกับไฟร์วอลล์: คำสั่งบนช่องคำสั่ง FTP (21 / TCP) ต่อรองพอร์ตชั่วคราวเพื่อใช้สำหรับคำสั่งโอนต่อไป ด้วยเหตุนี้ไฟร์วอลล์ที่มีสถานะจึงต้องสอดแนมคำสั่งเหล่านั้นและตอบกลับเพื่ออนุญาตให้ใช้พอร์ตที่เพียงพอล่วงหน้าชั่วคราว

บน Linux สิ่งนี้จัดเตรียมโดยโมดูลตัวช่วยเฉพาะโปรโตคอลซึ่งเป็นปลั๊กอินสำหรับคอนแทร็กระบบย่อย Netfilter ติดตามการเชื่อมต่อสำหรับ NAT และไฟร์วอลล์แบบ stateful ด้วย FTP เมื่อมีการเจรจาพอร์ต (ส่วนใหญ่เป็น PORT, EPRT, PASV หรือ EPSV) สำหรับการถ่ายโอนครั้งต่อไปได้เห็นบนพอร์ตคำสั่ง FTP ผู้ช่วยจะเพิ่มรายการที่มีอายุสั้นในตารางคอนแทร็กพิเศษ( ตารางความคาดหวังคอนแทร็ก ) ซึ่งจะ รอการเชื่อมต่อข้อมูลที่เกี่ยวข้องและที่คาดไว้ถัดไป

คำตอบของฉันใช้ที่ทันสมัย "เชื่อถือได้" จัดการตามที่อธิบายไว้ในบล็อกนี้เกี่ยวกับiptablesสำหรับ generalities และในnftablesวิกิพีเดียและman nftการจัดการในnftablesซึ่งแตกต่างจากiptables

การใช้กฎตัวช่วยและ nftables อย่างปลอดภัย

เคอร์เนลลินุกซ์ 4.7+ (ซึ่งรวมถึง 4.19) กำลังใช้วิธีการรักษาความปลอดภัยโดยค่าเริ่มต้น: การโหลดโมดูลตัวช่วย (ที่นี่ FTP) จะไม่ทำให้การสอดแนมแพ็กเก็ตทั้งหมดที่มีต้นทาง TCP หรือพอร์ตปลายทาง 21 อีกต่อไปจนกว่าจะระบุnftables (หรือiptables ) บอกว่ามันควรสอดแนมในกรณีใด (จำกัด ) วิธีนี้หลีกเลี่ยงการใช้งาน CPU ที่ไม่จำเป็นและอนุญาตให้เปลี่ยนได้ตลอดเวลาที่พอร์ต FTP เพื่อสอดแนมเพียงแค่เปลี่ยนกฎ (หรือชุด) เล็กน้อย

ส่วนแรกคือการประกาศกระแสที่สามารถกระตุ้นการสอดแนม มันแตกต่างกันในการจัดการnftablesกว่าในiptables ที่นี่มีการประกาศโดยใช้ct helperวัตถุที่มีสถานะและตัวกรองเพื่อเปิดใช้งานจะต้องทำหลังจากconntrack (ในขณะที่iptablesต้องมีการดำเนินการก่อน) man nftบอก:

ไม่เหมือนกับ iptables การกำหนดตัวช่วยจำเป็นต้องดำเนินการหลังจากการค้นหาคอนแทร็กเสร็จสิ้นแล้วตัวอย่างเช่นโดยมีลำดับความสำคัญของ hook 0 เริ่มต้น

nft add ct helper ip my_nat ftp-incoming '{ type "ftp" protocol tcp; }'

nft add chain ip my_nat my_helpers '{ type filter hook prerouting priority 10; }'
nft add rule ip my_nat my_helpers iif eth0 ip daddr 192.168.1.2 tcp dport 21 ct helper set ftp-incoming

ฉันเลือกตารางเดียวกัน แต่สามารถสร้างขึ้นในตารางอื่นได้ตราบใดที่การประกาศออบเจ็กต์ที่ระบุสถานะและกฎที่อ้างถึงอยู่ในตารางเดียวกัน

แน่นอนเราสามารถเลือกกฎที่เข้มงวดน้อยกว่าได้ การแทนที่กฎสุดท้ายด้วยกฎต่อไปนี้จะมีผลเช่นเดียวกับโหมดเดิม:

nft add rule ip my_nat my_helpers tcp dport 21 ct helper set ftp-incoming

สำหรับการอ้างอิงเท่านั้นโหมดดั้งเดิมที่ไม่ควรใช้อีกต่อไปไม่จำเป็นต้องใช้กฎข้างต้น แต่เป็นเพียงการสลับนี้ (และการโหลดโมดูลเคอร์เนลที่เกี่ยวข้องด้วยตนเอง):

sysctl -w net.netfilter.nf_conntrack_helper=1

nf_nat_ftpโหลดความมั่นใจแล้ว

โมดูลเคอร์เนลจะโหลดโดยอัตโนมัติด้วยการพึ่งพาที่สร้างขึ้นโดยnf_conntrack_ftp ct helper ... type "ftp"นั่นไม่ใช่กรณีnf_nat_ftpแต่จำเป็นต้องเปิดใช้งานการเชื่อมต่อแพ็คเก็ตในพอร์ตคำสั่งเมื่อเสร็จสิ้น NAT บนพอร์ตกระแสข้อมูล

ตัวอย่างเช่นหากต้องการnf_nat_ftpดึงโมดูลเมื่อใดก็ตามที่nf_conntrack_ftpมีการโหลดไฟล์/etc/modprobe.d/local-nat-ftp.confสามารถเพิ่มด้วยเนื้อหานี้:

install nf_conntrack_ftp /sbin/modprobe --ignore-install nf_conntrack_ftp; /sbin/modprobe --ignore-install nf_nat_ftp

หรือเพียงแค่เพิ่มตัวอย่างเช่น/etc/modules-load.d/local-nat-ftp.conf:

nf_nat_ftp

อย่างไรก็ตามตอนนี้ควรทำคำสั่งนี้เพื่อให้แน่ใจว่าโหลดแล้ว:

modprobe nf_nat_ftp

เกี่ยวกับไฟร์วอลล์

นี่คือหมายเหตุเพิ่มเติมสำหรับไฟร์วอลล์ นอกจากนี้ยังสามารถไฟร์วอลล์กฎระเบียบที่มีข้อ จำกัด บางอย่างแทนที่จะปล่อยให้ไหลใหม่ ๆ ติดแท็กเป็นที่เกี่ยวข้องโดยconntrack

ตัวอย่างเช่นแม้ว่าโมดูลตัวช่วย FTP จะจัดการทั้งโหมดพาสซีฟและโหมดแอคทีฟหากด้วยเหตุผลบางประการเราต้องการอนุญาตเฉพาะโหมดพาสซีฟ (ที่มีการเชื่อมต่อข้อมูลจากไคลเอนต์ไปยังเซิร์ฟเวอร์) และไม่ "ใช้งานอยู่" ftp (การเชื่อมต่อข้อมูลจากพอร์ตต้นทางของเซิร์ฟเวอร์ 20 ถึง ไคลเอนต์) สามารถใช้ตัวอย่างเช่นกฎเหล่านี้ในส่วนของไฟร์วอลล์ของชุดกฎแทนที่จะเป็นแบบปกติct state established,related accept:

ct state established accept
ct state related ct helper "ftp" iif eth0 oif eth1 tcp sport 1024-65535 accept
ct state related ct helper "ftp" drop
ct state related accept 

ยอมรับโฟลว์ที่เกี่ยวข้องประเภทอื่น ๆที่ไม่เกี่ยวข้องกับ FTP (หรืออาจแยกเพิ่มเติมอีกต่างหาก)


ตัวอย่างการจัดการโดยผู้ช่วยเหลือ

ที่นี่ (ในสภาพแวดล้อมจำลอง) สองเป็นconntrackรายการของเหตุการณ์ที่วัดบนความคาดหวังของตารางและconntrackตารางที่มีกฎของ OP + กฎข้างต้นเพิ่มเติมกับลูกค้าอินเทอร์เน็ต 203.0.113.101 ทำ FTP ในโหมดเรื่อย ๆ กับที่อยู่ IP ของเราเตอร์สาธารณะ 192.0 2.2 และใช้คำสั่ง LIST หลังจากเข้าสู่ระบบ:

# conntrack -E expect
    [NEW] 300 proto=6 src=203.0.113.101 dst=192.0.2.2 sport=0 dport=37157 mask-src=0.0.0.0 mask-dst=0.0.0.0 sport=0 dport=65535 master-src=203.0.113.101 master-dst=192.0.2.2 sport=50774 dport=21 class=0 helper=ftp
[DESTROY] 300 proto=6 src=203.0.113.101 dst=192.0.2.2 sport=0 dport=37157 mask-src=0.0.0.0 mask-dst=0.0.0.0 sport=0 dport=65535 master-src=203.0.113.101 master-dst=192.0.2.2 sport=50774 dport=21 class=0 helper=ftp

พร้อมกัน:

# conntrack -E
    [NEW] tcp      6 120 SYN_SENT src=203.0.113.101 dst=192.0.2.2 sport=50774 dport=21 [UNREPLIED] src=192.168.1.2 dst=192.168.1.1 sport=21 dport=50774 helper=ftp
 [UPDATE] tcp      6 60 SYN_RECV src=203.0.113.101 dst=192.0.2.2 sport=50774 dport=21 src=192.168.1.2 dst=192.168.1.1 sport=21 dport=50774 helper=ftp
 [UPDATE] tcp      6 432000 ESTABLISHED src=203.0.113.101 dst=192.0.2.2 sport=50774 dport=21 src=192.168.1.2 dst=192.168.1.1 sport=21 dport=50774 [ASSURED] helper=ftp
    [NEW] tcp      6 120 SYN_SENT src=203.0.113.101 dst=192.0.2.2 sport=55835 dport=37157 [UNREPLIED] src=192.168.1.2 dst=192.168.1.1 sport=37157 dport=55835
 [UPDATE] tcp      6 60 SYN_RECV src=203.0.113.101 dst=192.0.2.2 sport=55835 dport=37157 src=192.168.1.2 dst=192.168.1.1 sport=37157 dport=55835
 [UPDATE] tcp      6 432000 ESTABLISHED src=203.0.113.101 dst=192.0.2.2 sport=55835 dport=37157 src=192.168.1.2 dst=192.168.1.1 sport=37157 dport=55835 [ASSURED]
 [UPDATE] tcp      6 120 FIN_WAIT src=203.0.113.101 dst=192.0.2.2 sport=55835 dport=37157 src=192.168.1.2 dst=192.168.1.1 sport=37157 dport=55835 [ASSURED]
 [UPDATE] tcp      6 30 LAST_ACK src=203.0.113.101 dst=192.0.2.2 sport=55835 dport=37157 src=192.168.1.2 dst=192.168.1.1 sport=37157 dport=55835 [ASSURED]
 [UPDATE] tcp      6 120 TIME_WAIT src=203.0.113.101 dst=192.0.2.2 sport=55835 dport=37157 src=192.168.1.2 dst=192.168.1.1 sport=37157 dport=55835 [ASSURED]

การเริ่มต้นของความคาดหวังproto=6 src=203.0.113.101 dst=192.0.2.2 sport=0 dport=37157บอกว่าการเชื่อมต่อ TCP ถัดไปจาก 203.0.113.101:* ถึง 192.0.2.2:37157 จะเชื่อมโยง (สถานะที่เกี่ยวข้อง ) กับการเชื่อมต่อ FTP ไม่สามารถมองเห็นได้โดยตรง แต่เนื่องจากมีการโหลดโมดูลตัวช่วย NAT FTP ด้วยเช่นกันข้อมูลจริงที่ส่งโดยเซิร์ฟเวอร์เพื่อตอบสนองต่อคำสั่ง PASV / EPSV จึงถูกดักจับและแปลดังนั้นไคลเอนต์จึงเชื่อมต่อกับ 192.0.2.2 แทนที่จะเป็น 192.168.1.2 ซึ่งจะมี แน่นอนล้มเหลวในไคลเอนต์

แม้ว่าโฟลว์ที่สอง ( บรรทัดใหม่ที่สอง) จะไม่มีกฎที่ชัดเจนในmy_preroutingแต่ก็ประสบความสำเร็จ DNAT ไปยังเซิร์ฟเวอร์หลังเราเตอร์


หมายเหตุ

  • หากพอร์ตควบคุม FTP ถูกเข้ารหัส ( AUTH TLS... ) โมดูลตัวช่วยจะไม่สามารถสอดแนมพอร์ตที่เจรจาได้อีกต่อไปและไม่สามารถใช้งานได้ เราต้องใช้การกำหนดค่าช่วงของพอร์ตที่สงวนไว้ทั้งในการกำหนดค่าเซิร์ฟเวอร์ FTP และเราเตอร์ไฟร์วอลล์ / NAT และกำหนดค่าเซิร์ฟเวอร์เพื่อให้ส่งที่อยู่ IP ที่ถูกต้อง (สาธารณะ) เมื่อทำการเจรจาแทนที่จะเป็นของตัวเอง และโหมด FTP ที่ใช้งานอยู่จะไม่สามารถใช้งานได้หากเซิร์ฟเวอร์ไม่มีเส้นทางไปยังอินเทอร์เน็ต (ตามที่ปรากฏที่นี่)

  • nitpicking: ลำดับความสำคัญของการกำหนดเส้นทางล่วงหน้าที่ 10 ทำให้มั่นใจได้ว่าแม้กระทั่งสำหรับเคอร์เนล <4.18 แต่ NAT ก็เกิดขึ้นแล้วสำหรับโฟลว์ใหม่ (OP เลือก hook prerouting priority 0 แทนที่จะเป็น -100 ตามปกติเนื่องจากสิ่งนี้ไม่ค่อยมีความสำคัญใน nftables) ดังนั้นจึงสามารถใช้งานdaddr 192.168.1.2ได้ หากลำดับความสำคัญคือ 0 (หรือต่ำกว่า 0) อาจเป็นไปได้ (ไม่ได้รับการตรวจสอบ) กฎจะเห็นแพ็กเก็ตแรกที่ยังไม่ได้ระบุด้วยที่อยู่ปลายทาง IP สาธารณะ แต่จะจับแพ็กเก็ตต่อไปนี้ของโฟลว์เดียวกันเนื่องจากมีการจัดการ โดยตรงโดยconntrackที่ลำดับความสำคัญ -200 รักษาความปลอดภัยและใช้งานได้ดีขึ้น 10 อันที่จริงสิ่งนี้ไม่เกี่ยวข้องเนื่องจากเคอร์เนล 4.18 (ดูการอ้างอิงการกระทำในแพตช์ที่รอดำเนินการนี้ ) ซึ่งลำดับความสำคัญของ NAT จะเกี่ยวข้องเฉพาะสำหรับการเปรียบเทียบระหว่างกลุ่ม Nat หลายตัวเท่านั้น (และอนุญาตให้ผสม NAT ใน iptables ดั้งเดิมกับ nftables)

GunnarHolm Sep 04 2020 at 11:56

หลังจากการลองผิดลองถูกฉันได้พบกับ nftables.conf ต่อไปนี้ซึ่งทำงานได้ตามที่ตั้งใจไว้และรองรับ NAT ทั้งสองทิศทางรวมทั้งส่งออกพอร์ตทั้งหมดยกเว้นพอร์ตเดียวบนเซิร์ฟเวอร์ของฉันไปยังเครือข่าย พอร์ต 2222 ยังคงใช้เป็น "แบ็คดอร์" ไปยังเกตเวย์เพื่อใช้ถ้าฉันต้องการเข้าถึงอีกครั้ง :-)

table ip my_nat {
        ct helper ftp-incoming {
                type "ftp" protocol tcp
                l3proto ip
        }

        chain my_prerouting {
                type nat hook prerouting priority 0; policy accept;
                iifname "eth0" tcp dport { 2222 } dnat to :ssh
                iifname "eth0" tcp dport { 1-2221, 2223-65535 } dnat to 192.168.0.2
        }

        chain my_postrouting {
                type nat hook postrouting priority 100; policy accept;
                ip daddr 192.168.0.2 masquerade
                oifname "eth0" masquerade
        }

        chain my_helpers {
                type filter hook prerouting priority 10; policy accept;
                iif "eth0" ip daddr 192.168.0.2 tcp dport ftp ct helper set "ftp-incoming"
        }

}