ตรวจสอบชุด IP แล้ว
มีคำตอบที่กล่าวถึงชุด IP แล้ว อย่างไรก็ตามมันค่อนข้างเป็นมิติเดียวที่จะมุ่งเน้นไปที่ประสิทธิภาพที่เพิ่มขึ้นตามกฎดั้งเดิมและความจริงที่ว่าชุด IP ช่วยลดปัญหาที่เรามีกับที่อยู่ IP ส่วนบุคคลจำนวนมากที่ไม่สามารถแสดงได้อย่างง่ายดายในฐานะซับเน็ตในสัญกรณ์ CIDR
สัญลักษณ์ที่ใช้ด้านล่าง
สำหรับipset
ผมจะใช้สัญกรณ์อ่านโดยและเขียนโดยipset restore
ipset save
ตามลําดับสำหรับiptables
(และip6tables
) กฎผมจะใช้สัญกรณ์เป็นอ่านโดยและเขียนโดยiptables-restore
iptables-save
ทำให้สั้นลงและทำให้ฉันสามารถเน้นกฎIPv4-only (prefixed -4
) หรือ IPv6-only (prefixed -6
) ที่เป็นไปได้
ในตัวอย่างเราจะเบี่ยงเบนการไหลของแพ็กเก็ตไปเป็นสายโซ่อื่น เชนจะถือว่ามีอยู่ ณ จุดนั้นดังนั้นบรรทัดในการสร้างเชนส์จะไม่ถูกสร้างขึ้น (หรือเป็นชื่อตารางที่กล่าวถึงหรือคำสั่งCOMMIT
-ted เมื่อสิ้นสุด)
ชุด IP ขั้นสูง
ชุด IP สามารถทำได้มากกว่าที่กล่าวไว้ในคำตอบอื่น ๆและคุณควรอ่านเอกสารชุด IP ( ipset(8)
) พร้อมด้วยiptables-extensions(8)
นอกเหนือจากรายการสั้น ๆ นี้ที่นี่
ตัวอย่างเช่นฉันจะมุ่งเน้นไปที่ชุดของสามประเภท: hash:ip
, hash:net
และlist:set
, แต่มีมากกว่านั้นและพวกเขาทั้งหมดมีกรณีการใช้ที่ถูกต้อง
เช่นคุณสามารถตรงกับหมายเลขพอร์ตไม่ได้เป็นเพียงที่อยู่ IP
การบันทึกและกู้คืนชุด IP เช่นเดียวกับiptables-save
และiptables-restore
คุณสามารถสร้างการประกาศชุด IP เป็นกลุ่มและนำเข้าได้โดยการส่งไปipset restore
ยัง ipset -exist restore
หากคุณต้องการที่จะทำให้คำสั่งของคุณมีความยืดหยุ่นมากขึ้นกับรายการที่มีอยู่แล้วให้ใช้
หากกฎของคุณอยู่ในไฟล์ที่เรียกว่าdefault.set
คุณจะใช้:
ipset -exist restore < default.set
ไฟล์เช่นนั้นสามารถมีรายการที่จะcreate
ตั้งค่าและไปยังadd
รายการต่างๆ แต่โดยทั่วไปคำสั่งส่วนใหญ่จากบรรทัดคำสั่งดูเหมือนจะมีรุ่นที่สอดคล้องกันในไฟล์ ตัวอย่าง (การสร้างชุดเซิร์ฟเวอร์ DNS):
create dns4 hash:ip family inet
create dns6 hash:ip family inet6
# Google DNS servers
add dns4 8.8.8.8
add dns4 8.8.4.4
add dns6 2001:4860:4860::8888
add dns6 2001:4860:4860::8844
ที่นี่หนึ่งชุดถูกสร้างขึ้นสำหรับ IPv4 ( dns4
) และอีกชุดสำหรับ IPv6 ( dns6
)
หมดเวลากับชุด IP
หมดเวลาในชุด IP สามารถตั้งเป็นค่าเริ่มต้นต่อชุดและต่อรายการ สิ่งนี้มีประโยชน์มากสำหรับสถานการณ์ที่คุณต้องการบล็อกใครบางคนชั่วคราว (เช่นสำหรับการสแกนพอร์ตหรือพยายามบังคับเซิร์ฟเวอร์ SSH ของคุณ)
วิธีการทำงานดังต่อไปนี้ (ค่าเริ่มต้นระหว่างการสร้างชุด IP):
create ssh_loggedon4 hash:ip family inet timeout 5400
create ssh_loggedon6 hash:ip family inet6 timeout 5400
create ssh_dynblock4 hash:ip family inet timeout 1800
create ssh_dynblock6 hash:ip family inet6 timeout 1800
เราจะกลับไปที่ชุดเฉพาะเหล่านี้ด้านล่างและเหตุผลว่าทำไมพวกเขาถึงตั้งค่าแบบที่พวกเขาเป็น
หากคุณต้องการตั้งค่าการหมดเวลาสำหรับที่อยู่ IP เฉพาะคุณสามารถพูดได้ว่า:
add ssh_dynblock4 1.2.3.4 timeout 7200
หากต้องการบล็อก IP 1.2.3.4 เป็นเวลาสองชั่วโมงแทนการตั้งค่าเริ่มต้นครึ่งชั่วโมง
หากคุณต้องดูสิ่งipset save ssh_dynblock4
นั้นหลังจากผ่านไปครู่หนึ่งคุณจะเห็นบางสิ่งบางอย่างตามแนวของ:
create ssh_dynblock4 hash:ip family inet hashsize 1024 maxelem 65536 timeout 1800
add ssh_dynblock4 1.2.3.4 timeout 6954
คำเตือนหมดเวลา
- หมดเวลาเป็นคุณสมบัติในชุดที่กำหนด หากไม่ได้สร้างชุดที่มีการสนับสนุนการหมดเวลาคุณจะได้รับข้อผิดพลาด (เช่น
Kernel error received: Unknown error -1
)
- หมดเวลาจะได้รับในไม่กี่วินาที ตัวอย่างเช่นใช้นิพจน์ทางคณิตศาสตร์ของ Bash เพื่อรับจากนาทีเป็นวินาที เช่น:
sudo ipset add ssh_dynblock4 1.2.3.4 timeout $((120*60))
การตรวจสอบว่ามีรายการอยู่ในชุด IP ที่กำหนดหรือไม่
ภายในสคริปต์ของคุณจะเป็นประโยชน์ในการดูว่ามีรายการอยู่แล้ว สิ่งนี้สามารถทำได้โดยipset test
ที่คืนค่าศูนย์ถ้ารายการมีอยู่และไม่เป็นศูนย์ ดังนั้นการตรวจสอบตามปกติสามารถนำไปใช้ในสคริปต์:
if ipset test dns4 8.8.8.8; then
echo "Google DNS is in the set"
fi
อย่างไรก็ตามในหลายกรณีคุณจะต้องการใช้-exist
สวิตช์ipset
เพื่อไม่ให้บ่นเกี่ยวกับรายการที่มีอยู่
การเติมชุด IP จากiptables
กฎ
ในความคิดของฉันนี้เป็นหนึ่งในคุณสมบัตินักฆ่าของชุด IP ไม่เพียง แต่คุณสามารถจับคู่กับรายการของชุด IP คุณยังสามารถเพิ่มรายการใหม่ไปยังชุด IP ที่มีอยู่
ตัวอย่างเช่นในคำตอบของคำถามนี้คุณมี:
-A INPUT -p tcp -i eth0 -m state --state NEW --dport 22 -m recent --update --seconds 15 -j DROP
-A INPUT -p tcp -i eth0 -m state --state NEW --dport 22 -m recent --set -j ACCEPT
... ด้วยความตั้งใจที่จะ จำกัด การเชื่อมต่ออัตราพยายาม SSH (พอร์ต TCP 22) โมดูลที่ใช้recent
ติดตามความพยายามเชื่อมต่อล่าสุด อย่างไรก็ตามแทนที่จะเป็นstate
โมดูลฉันชอบconntrack
โมดูลมากกว่า
# Say on your input chain of the filter table you have
-A INPUT -i eth+ -p tcp --dport ssh -j SSH
# Then inside the SSH chain you can
# 1. create an entry in the recent list on new connections
-A SSH -m conntrack --ctstate NEW -m recent --set --name tarpit
# 2. check whether 3 connection attempts were made within 2 minutes
# and if so add or update an entry in the ssh_dynblock4 IP set
-4 -A SSH -m conntrack --ctstate NEW -m recent --rcheck --seconds 120 --hitcount 3 --name tarpit -j SET --add-set ssh_dynblock4 src --exist
-6 -A SSH -m conntrack --ctstate NEW -m recent --rcheck --seconds 120 --hitcount 3 --name tarpit -j SET --add-set ssh_dynblock6 src --exist
# 3. last but not least reject the packets if the source IP is in our
# IP set
-4 -A SSH -m set --match-set ssh_dynblock4 src -j REJECT
-6 -A SSH -m set --match-set ssh_dynblock6 src -j REJECT
ในกรณีนี้ฉันกำลังเปลี่ยนทิศทางการไหลไปยังSSH
สายโซ่ดังกล่าวซึ่งฉันไม่ต้องทำซ้ำตัวเองด้วย-p tcp --dport ssh
สำหรับกฎทุกข้อ
หากต้องการย้ำ:
-m set
ทำให้iptables
ทราบว่าเราใช้สวิตช์จากset
โมดูล (ซึ่งจัดการชุด IP)
--match-set ssh_dynblock4 src
บอกiptables
ให้ตรงกับที่อยู่ต้นทาง ( src
) กับชุดชื่อ ( ssh_dynblock4
)
- สิ่งนี้สอดคล้องกับ
sudo ipset test ssh_dynblock4 $IP
(ที่$IP
มีที่อยู่ IP ต้นทางสำหรับแพ็คเก็ต)
-j SET --add-set ssh_dynblock4 src --exist
เพิ่มหรือปรับปรุงแหล่งที่มา ( src
ที่อยู่) ssh_dynblock4
จากแพ็คเก็ตลงไปในชุด หากมีรายการอยู่ ( --exist
) รายการนั้นจะได้รับการอัปเดต
- สิ่งนี้สอดคล้องกับ
sudo ipset -exist add ssh_dynblock4 $IP
(ที่$IP
มีที่อยู่ IP ต้นทางสำหรับแพ็คเก็ต)
หากคุณต้องการที่จะตรงกับที่อยู่เป้าหมาย / ปลายทางแทนคุณต้องการใช้แทนdst
src
ศึกษาคู่มือสำหรับตัวเลือกเพิ่มเติม
ชุดเซต
ชุด IP สามารถมีชุดอื่น ๆ ตอนนี้ถ้าคุณติดตามบทความจนถึงที่นี่คุณจะสงสัยว่าเป็นไปได้หรือไม่ที่จะรวมชุดต่างๆ และแน่นอนมันเป็น สำหรับชุด IP จากด้านบนเราสามารถสร้างชุดข้อต่อสองชุดssh_dynblock
และssh_loggedon
ตามลำดับเพื่อให้มีชุด IPv4-only และ IPv6-only เท่านั้น:
create ssh_loggedon4 hash:ip family inet timeout 5400
create ssh_loggedon6 hash:ip family inet6 timeout 5400
create ssh_dynblock4 hash:ip family inet timeout 1800
create ssh_dynblock6 hash:ip family inet6 timeout 1800
# Sets of sets
create ssh_loggedon list:set
create ssh_dynblock list:set
# Populate the sets of sets
add ssh_loggedon ssh_loggedon4
add ssh_loggedon ssh_loggedon6
add ssh_dynblock ssh_dynblock4
add ssh_dynblock ssh_dynblock6
และคำถามต่อไปที่ควรปรากฏในใจของคุณคือสิ่งนี้ช่วยให้เราสามารถจับคู่และจัดการชุด IP ในรูปแบบที่ไม่เชื่อเรื่องพระเจ้าได้หรือไม่
และคำตอบก็คือดังก้อง: ใช่! (อนิจจานี่ไม่ใช่เอกสารครั้งสุดท้ายที่ฉันตรวจสอบอย่างชัดเจน)
ดังนั้นกฎจากส่วนก่อนหน้านี้สามารถเขียนใหม่เพื่ออ่าน:
-A INPUT -i eth+ -p tcp --dport ssh -j SSH
-A SSH -m conntrack --ctstate NEW -m recent --set --name tarpit
-A SSH -m conntrack --ctstate NEW -m recent --rcheck --seconds 120 --hitcount 3 --name tarpit -j SET --add-set ssh_dynblock src --exist
-A SSH -m set --match-set ssh_dynblock src -j REJECT
ซึ่งกระชับกว่านี้มาก และใช่นี่คือการลองและทดสอบและใช้งานได้อย่างมีเสน่ห์
รวบรวมทุกอย่างเข้าด้วยกัน: การป้องกันกำลังดุร้ายของ SSH
บนเซิร์ฟเวอร์ของฉันฉันมีสคริปต์ทำงานเป็นcron
งานที่ใช้ชื่อโฮสต์จำนวนมากและแก้ไขที่อยู่ IP จากนั้นป้อนลงในชุด IP สำหรับ "โฮสต์ที่เชื่อถือได้" แนวคิดก็คือโฮสต์ที่เชื่อถือได้จะพยายามเข้าสู่เซิร์ฟเวอร์มากขึ้นและไม่จำเป็นต้องถูกบล็อกออกนานเท่าที่คนอื่น ๆ
ในทางกลับกันฉันมีทั้งประเทศที่ถูกบล็อกไม่ให้เชื่อมต่อกับเซิร์ฟเวอร์ SSH ของฉันด้วยข้อยกเว้น (ที่เป็นไปได้) ของโฮสต์ที่เชื่อถือได้ (เช่นลำดับของกฎสำคัญ)
อย่างไรก็ตามนั่นเป็นแบบฝึกหัดสำหรับผู้อ่าน ที่นี่ฉันต้องการเพิ่มโซลูชันที่เรียบร้อยที่จะใช้ชุดที่มีอยู่ในssh_loggedon
ชุดเพื่ออนุญาตการเชื่อมต่อที่ตามมาพยายามส่งผ่านและไม่ต้องมีการตรวจสอบข้อเท็จจริงเช่นเดียวกับแพ็กเก็ตอื่น ๆ
สิ่งสำคัญคือต้องจดจำการหมดเวลาเริ่มต้นของ 90 นาทีssh_loggedon
และ 30 นาทีssh_dynblock
เมื่อดูiptables
กฎต่อไปนี้:
-A INPUT -i eth+ -p tcp --dport ssh -j SSH
-A SSH -m set --match-set ssh_loggedon src -j ACCEPT
-A SSH -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A SSH -m conntrack --ctstate NEW -m recent --set --name tarpit
-A SSH -m conntrack --ctstate NEW -m recent --rcheck --seconds 120 --hitcount 3 --name tarpit -j SET --add-set ssh_dynblock src --exist
-A SSH -m set --match-set ssh_dynblock src -j REJECT
ถึงตอนนี้คุณควรถามตัวคุณเองว่าการเชื่อมต่อที่อยู่ IP สิ้นสุดลงในชุดssh_loggedon
ย่อยอย่างไร อ่านต่อ ...
โบนัส: เพิ่ม IP ที่คุณลงชื่อเข้าใช้ด้วยระหว่างการเข้าสู่ระบบ SSH
หากคุณทดลองsshrc
และกับเพื่อน ๆ คุณจะได้เรียนรู้ข้อบกพร่องของมัน แต่ PAM มาเพื่อช่วยเหลือ โมดูลที่ชื่อpam_exec.so
ช่วยให้เราสามารถเรียกใช้สคริปต์ระหว่างการเข้าสู่ระบบ SSH ที่จุดที่เรารู้ว่าผู้ใช้เป็นที่ยอมรับ
ใน/etc/pam.d/sshd
ด้านล่างpam_env
และpam_selinux
รายการเพิ่มบรรทัดต่อไปนี้:
session optional pam_exec.so stdout /path/to/your/script
และตรวจสอบให้แน่ใจว่าเวอร์ชันของสคริปต์ของคุณ ( /path/to/your/script
ด้านบน) มีอยู่และสามารถใช้งานได้
PAM ใช้ตัวแปรสภาพแวดล้อมเพื่อสื่อสารสิ่งที่เกิดขึ้นเพื่อให้คุณสามารถใช้สคริปต์อย่างง่ายเช่นนี้:
#!/bin/bash
# When called via pam_exec.so ...
SETNAME=ssh_loggedon
if [[ "$PAM_TYPE" == "open_session" ]] && [[ -n "$PAM_RHOST" ]]; then
[[ "x$PAM_RHOST" != "x${PAM_RHOST//:/}" ]] && SETNAME="${SETNAME}6" || SETNAME="${SETNAME}4"
ipset -exist add $SETNAME "$PAM_RHOST"
fi
น่าเสียดายที่ipset
ยูทิลิตีดูเหมือนจะไม่มีตัวกรอง netfilter ในตัว ดังนั้นเราจำเป็นต้องแยกแยะระหว่างชุด IPv4 และ IPv6 IP เมื่อเพิ่มรายการของเรา มิฉะนั้นipset
จะถือว่าเราต้องการเพิ่มอีกชุดหนึ่งให้กับชุดของชุดแทนที่จะเป็น IP และแน่นอนว่าไม่น่าจะมีชุดชื่อหลังจาก IP :)
ดังนั้นเราจึงตรวจสอบ:
ที่อยู่ IP และต่อท้าย6
ชื่อชุดในกรณีดังกล่าวและ4
อื่น ๆ
ตอนจบ.