Xem lại bộ IP
Đã có một câu trả lời đề cập đến bộ IP. Tuy nhiên, nó tập trung vào một chiều ở chỗ nó tập trung vào hiệu suất đạt được so với các quy tắc cổ điển và thực tế là các bộ IP giảm thiểu vấn đề mà người ta gặp phải với rất nhiều địa chỉ IP riêng lẻ không thể được biểu thị như một mạng con trong ký hiệu CIDR.
Ký hiệu được sử dụng dưới đây
Vì ipset
tôi sẽ sử dụng ký hiệu được đọc ipset restore
và viết bởi ipset save
.
Tương ứng với các quy tắc iptables
(và ip6tables
) tôi sẽ sử dụng ký hiệu như đã đọc iptables-restore
và viết bởi iptables-save
. Điều này tạo ra một ký hiệu ngắn hơn và nó cho phép tôi làm nổi bật các quy tắc tiềm năng chỉ có IPv4 (tiền tố -4
) hoặc chỉ IPv6 (tiền tố -6
).
Trong một số ví dụ, chúng tôi sẽ chuyển luồng gói vào một chuỗi khác. Chuỗi được giả sử tồn tại tại thời điểm đó, do đó, các dòng để tạo chuỗi không được tạo ra (tên bảng cũng được đề cập hoặc các lệnh được COMMIT
đặt ở cuối).
Bộ IP nâng cao
Các bộ IP có thể làm được nhiều hơn những gì được đề cập trong câu trả lời khác và bạn chắc chắn nên đọc tài liệu về bộ IP ( ipset(8)
) cùng với iptables-extensions(8)
mục nhập ngắn gọn này ở đây.
Ví dụ tôi sẽ chủ yếu tập trung vào ba loại thiết lập: hash:ip
, hash:net
và list:set
, nhưng có nhiều hơn những người và tất cả họ đều có trường hợp sử dụng hợp lệ.
Ví dụ, bạn cũng có thể khớp số cổng, không chỉ địa chỉ IP .
Lưu và khôi phục các bộ IP như với iptables-save
vàiptables-restore
Bạn có thể tạo các khai báo tập hợp IP hàng loạt và nhập chúng bằng cách đưa chúng vào ipset restore
. Nếu bạn muốn làm cho lệnh của mình trở nên linh hoạt hơn đối với các mục đã có, hãy sử dụng ipset -exist restore
.
Nếu quy tắc của bạn nằm trong một tệp có tên default.set
bạn sẽ sử dụng:
ipset -exist restore < default.set
Một tệp như thế có thể chứa các mục nhập để create
thiết lập và add
các mục nhập vào chúng. Nhưng nhìn chung hầu hết các lệnh từ dòng lệnh dường như có một phiên bản tương ứng trong các tệp. Ví dụ (tạo một bộ máy chủ 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
Ở đây, một bộ được tạo cho IPv4 ( dns4
) và một cho IPv6 ( dns6
).
Hết giờ trên bộ IP
Thời gian chờ trong bộ IP có thể được đặt làm mặc định cho mỗi bộ và cũng cho mỗi mục nhập. Điều này rất hữu ích cho các tình huống mà bạn muốn chặn một người tạm thời (ví dụ như quét cổng hoặc cố gắng vũ phu máy chủ SSH của bạn).
Cách thức hoạt động như sau (mặc định trong quá trình tạo bộ 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
Chúng tôi sẽ quay trở lại các bộ cụ thể dưới đây và lý do tại sao chúng được đặt theo cách chúng.
Nếu bạn muốn đặt thời gian chờ của mình cho một địa chỉ IP cụ thể, bạn có thể chỉ cần nói:
add ssh_dynblock4 1.2.3.4 timeout 7200
Để chặn IP 1.2.3.4 trong hai giờ thay vì nửa giờ mặc định (đã đặt).
Nếu bạn đã xem xét điều đó ipset save ssh_dynblock4
sau một thời gian ngắn, bạn sẽ thấy một cái gì đó dọc theo dòng:
create ssh_dynblock4 hash:ip family inet hashsize 1024 maxelem 65536 timeout 1800
add ssh_dynblock4 1.2.3.4 timeout 6954
Hết thời gian chờ
- thời gian chờ là một tính năng trên bất kỳ bộ đã cho. Nếu bộ không được tạo với hỗ trợ hết thời gian, bạn sẽ gặp lỗi (ví dụ
Kernel error received: Unknown error -1
).
- thời gian chờ được đưa ra trong vài giây. Ví dụ, sử dụng các biểu thức số học Bash để có được từ vài phút đến vài giây. Ví dụ:
sudo ipset add ssh_dynblock4 1.2.3.4 timeout $((120*60))
Kiểm tra xem một mục có tồn tại trong một bộ IP nhất định không
Bên trong các tập lệnh của bạn, có thể hữu ích để xem liệu một mục đã tồn tại. Điều này có thể đạt được ipset test
mà trả về 0 nếu mục nhập tồn tại và khác không. Vì vậy, kiểm tra thông thường có thể được áp dụng trong một tập lệnh:
if ipset test dns4 8.8.8.8; then
echo "Google DNS is in the set"
fi
Tuy nhiên, trong nhiều trường hợp, bạn sẽ muốn sử dụng công -exist
tắc ipset
để chỉ đạo nó không phàn nàn về các mục hiện có.
Đặt các bộ IP từ iptables
quy tắc
Theo tôi, đây là một trong những tính năng giết người của các bộ IP. Bạn không chỉ có thể khớp với các mục của bộ IP, bạn cũng có thể thêm các mục mới vào bộ IP hiện có.
Ví dụ trong câu trả lời cho câu hỏi này, bạn có:
-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
... Với ý định giới hạn tốc độ kết nối với SSH (TCP port 22). Các mô-đun được sử dụng recent
theo dõi các nỗ lực kết nối gần đây. Thay vì state
mô-đun, tôi thích conntrack
mô-đun, tuy nhiên.
# 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
Trong trường hợp này, tôi đang chuyển hướng dòng chảy sang SSH
chuỗi sao cho tôi không phải lặp lại chính mình -p tcp --dport ssh
cho mỗi quy tắc.
Để nhắc lại:
-m set
lưu iptables
ý rằng chúng tôi đang sử dụng các công tắc từ set
mô-đun (xử lý các bộ IP)
--match-set ssh_dynblock4 src
nói iptables
để khớp địa chỉ nguồn ( src
) với tập hợp được đặt tên ( ssh_dynblock4
)
- cái này tương ứng với
sudo ipset test ssh_dynblock4 $IP
(nơi $IP
chứa địa chỉ IP nguồn cho gói)
-j SET --add-set ssh_dynblock4 src --exist
thêm hoặc cập nhật địa chỉ nguồn ( src
) từ gói vào bộ IP ssh_dynblock4
. Nếu một mục tồn tại ( --exist
) nó sẽ chỉ được cập nhật.
- cái này tương ứng với
sudo ipset -exist add ssh_dynblock4 $IP
(nơi $IP
chứa địa chỉ IP nguồn cho gói)
Thay vào đó, nếu bạn muốn khớp địa chỉ đích / đích, bạn sẽ sử dụng dst
thay vì src
. Tham khảo hướng dẫn để có thêm lựa chọn.
Bộ
Bộ IP có thể chứa các bộ khác. Bây giờ nếu bạn theo dõi bài viết ở đây, bạn sẽ tự hỏi liệu có thể kết hợp các bộ không. Và tất nhiên nó là. Đối với các bộ IP ở trên, chúng ta có thể tạo hai bộ chung ssh_dynblock
và ssh_loggedon
tương ứng để chứa các bộ chỉ có IPv4 và chỉ IPv6:
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
Và câu hỏi tiếp theo sẽ xuất hiện trong đầu bạn là liệu điều này có cho phép chúng ta khớp và điều khiển các bộ IP theo kiểu không tin tưởng phiên bản IP hay không.
Và câu trả lời cho điều đó là một tiếng vang: CÓ! (than ôi, điều này không được ghi lại rõ ràng lần trước tôi đã kiểm tra)
Do đó, các quy tắc từ phần trước có thể được viết lại để đọc:
-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
mà ngắn gọn hơn nhiều Và vâng, điều này đã được thử và thử nghiệm và hoạt động như một lá bùa.
Kết hợp tất cả lại với nhau: SSH brute-force defence
Trên các máy chủ của tôi, tôi có một tập lệnh chạy như một cron
công việc lấy một loạt các tên máy chủ và phân giải chúng thành các địa chỉ IP, sau đó đưa nó vào tập hợp IP cho "máy chủ tin cậy". Ý tưởng là các máy chủ đáng tin cậy nhận được nhiều nỗ lực hơn để đăng nhập vào máy chủ và không nhất thiết phải bị chặn miễn là bất kỳ ai khác.
Ngược lại, tôi có toàn bộ các quốc gia bị chặn kết nối với máy chủ SSH của mình, ngoại trừ (tiềm năng) các máy chủ đáng tin cậy (nghĩa là thứ tự các quy tắc).
Tuy nhiên, đó là một bài tập cho người đọc. Ở đây tôi muốn thêm một giải pháp gọn gàng sẽ sử dụng các bộ có trong ssh_loggedon
bộ để cho phép các lần thử kết nối tiếp theo được truyền qua và không phải chịu sự giám sát tương tự như các gói khác.
Điều quan trọng cần nhớ là thời gian chờ mặc định là 90 phút ssh_loggedon
và 30 phút ssh_dynblock
khi xem các iptables
quy tắc sau :
-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
Bây giờ bạn nên tự hỏi làm thế nào địa chỉ IP kết nối kết thúc trong các ssh_loggedon
tập hợp con. Vì vậy, hãy đọc tiếp ...
Phần thưởng: thêm IP bạn đăng nhập trong khi đăng nhập SSH
Nếu bạn đã thử nghiệm với sshrc
và bạn bè, bạn sẽ biết về những thiếu sót của nó. Nhưng PAM đến giải cứu. Một mô-đun có tên pam_exec.so
cho phép chúng tôi gọi một tập lệnh trong khi đăng nhập SSH tại một điểm mà chúng tôi biết rằng người dùng được chấp nhận.
Ở /etc/pam.d/sshd
bên dưới pam_env
và pam_selinux
các mục thêm dòng sau:
session optional pam_exec.so stdout /path/to/your/script
và đảm bảo rằng phiên bản tập lệnh của bạn ( /path/to/your/script
ở trên) tồn tại và có thể thực thi được.
PAM sử dụng các biến môi trường để truyền đạt những gì đang diễn ra, vì vậy bạn có thể sử dụng một tập lệnh đơn giản như thế này:
#!/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
Thật không may, ipset
tiện ích dường như không có các bộ lọc mạng tích hợp sẵn. Vì vậy, chúng ta cần phân biệt giữa IP IPv4 và IPv6 khi thêm mục nhập của chúng tôi. Nếu không, ipset
sẽ giả sử chúng ta muốn thêm một bộ khác vào tập hợp các bộ, thay vì IP. Và tất nhiên, không chắc sẽ có một bộ được đặt tên theo IP :)
Vì vậy, chúng tôi kiểm tra :
địa chỉ IP và thêm 6
vào tên đã đặt trong trường hợp đó và 4
nếu không.
Kết thúc.