起因:今天有人不斷登入我主機,應該是拿我練習入侵。我也一直就有寫個自動拉黑指令碼的打算了,正好也用這個機會測試一下。
思路:用crontab建立計劃任務每1小時用awk過濾lastb裡顯示的上個小時裡失敗登入的IP地址並做統計,超過設定的次數就自動加入firewalld的黑名單裡並mail發郵件到我郵箱。
指令
[root@RHEL9 ~]# lastb | awk -v hourago="$(date --date='1 hour ago' '+%a %b %e %H:')" '$0~hourago{ip[$3]++}END{for (i in ip){if(ip[i]>11){system("firewall-cmd --add-source="i" --zone=block;firewall-cmd --runtime-to-permanent;echo \"$(hostname) ban "i" for connected "ip[i]" times in 1 hour\" >> /tmp/baninfo")}}}';[ -e /tmp/baninfo ] && cat /tmp/baninfo | mail -s "Ban Info" xxxxx@xx.com && rm -f /tmp/baninfo
crontab
[root@RHEL9 ~]# crontab -l
0 * * * * lastb | awk -v hourago="$(date --date='1 hour ago' '+\%a \%b \%e \%H:')" '$0~hourago{ip[$3]++}END{for (i in ip){if(ip[i]>11){system("firewall-cmd --add-source="i" --zone=block;firewall-cmd --runtime-to-permanent;echo \"$(hostname) ban "i" for connected "ip[i]" times in 1 hour\" >> /tmp/baninfo")}}}';[ -e /tmp/baninfo ] && cat /tmp/baninfo | mail -s "Ban Info" XXXXX@XX.com && rm -f /tmp/baninfo
shell自動寫入crontab
[root@RHEL9 ~]# cat ban.sh
#ban.sh
#Date: 2024-11-07
#!/bin/bash
cat << EOF >> /var/spool/cron/root
0 * * * * lastb | awk -v hourago="\$(date --date='1 hour ago' '+\%a \%b \%e \%H:')" '\$0~hourago{ip[\$3]++}END{for (i in ip){if(ip[i]>11){system("firewall-cmd --add-source="i" --zone=block;firewall-cmd --runtime-to-permanent;echo \"\$(hostname) ban "i" for connected "ip[i]" times in 1 hour\" >> /tmp/baninfo")}}}';[ -e /tmp/baninfo ] && cat /tmp/baninfo | mail -s "Ban Info" XXXXX@XX.com && rm -f /tmp/baninfo
EOF
目前還一直在被登入
[root@RHEL9 ~]# lastb | head
sycdl ssh:notty 159.89.207.36 Wed Nov 6 23:53 - 23:53 (00:00)
ss97 ssh:notty 159.89.207.36 Wed Nov 6 23:53 - 23:53 (00:00)
smy ssh:notty 159.89.207.36 Wed Nov 6 23:53 - 23:53 (00:00)
smy ssh:notty 159.89.207.36 Wed Nov 6 23:53 - 23:53 (00:00)
shuminzh ssh:notty 159.89.207.36 Wed Nov 6 23:53 - 23:53 (00:00)
shuminzh ssh:notty 159.89.207.36 Wed Nov 6 23:53 - 23:53 (00:00)
pyt ssh:notty 159.89.207.36 Wed Nov 6 23:52 - 23:52 (00:00)
pyt ssh:notty 159.89.207.36 Wed Nov 6 23:52 - 23:52 (00:00)
omnisky ssh:notty 159.89.207.36 Wed Nov 6 23:52 - 23:52 (00:00)
mrbai ssh:notty 159.89.207.36 Wed Nov 6 23:51 - 23:51 (00:00)
先用date指令調出上一個小時的時間
[root@RHEL9 ~]# date
Wed Nov 6 11:59:43 PM CST 2024
[root@RHEL9 ~]# date --date='1 hour ago'
Wed Nov 6 11:00:32 PM CST 2024
lastb的時間日期格式為 縮寫的星期名 縮寫的月份名 空格填充的日期(個位日期前面沒有0) 24小時的時間:分鐘
%a locale's abbreviated weekday name (e.g., Sun)
%b locale's abbreviated month name (e.g., Jan)
%e day of month, space padded; same as %_d
%H hour (00..23)
%M minute (00..59)
按小時過濾,分鐘這個引數用不上,但是測試的時候用的上。配置引數將date指令日期格式設定為與lastb相同
[root@RHEL9 ~]# lastb | head -1
lixiao ssh:notty 159.89.207.36 Thu Nov 7 00:33 - 00:33 (00:00)
[root@RHEL9 ~]# lastb | head -1 | sed -En 's/^.+\.[0-9]+ +(.+)$/\1/p'
Thu Nov 7 00:33 - 00:33 (00:00)
[root@RHEL9 ~]# date --date='1 hour ago' '+%a %b %e %H:%M'
Wed Nov 6 23:34
測試date指令在awk裡賦值並呼叫
[root@RHEL9 ~]# echo | awk -v hourago="$(date --date='1 hour ago' '+%a %b %e %H:%M')" '{print hourago}'
Wed Nov 6 23:40
用 ~ 匹配,$0 ~ hourago ,檢索 全文裡 包含 hourago 變數裡日期格式的行,目前檢索出的是上個小時按分鐘檢索出的登入我主機的行。
[root@RHEL9 ~]# lastb | awk -v hourago="$(date --date='1 hour ago' '+%a %b %e %H:%M')" '$0~hourago{print $0}'
fym ssh:notty 159.89.207.36 Wed Nov 6 23:49 - 23:49 (00:00)
csj20017 ssh:notty 159.89.207.36 Wed Nov 6 23:49 - 23:49 (00:00)
cjw ssh:notty 159.89.207.36 Wed Nov 6 23:49 - 23:49 (00:00)
[root@RHEL9 ~]# lastb | awk -v hourago="$(date --date='1 hour ago' '+%a %b %e %H:%M')" '$0~hourago{print $0}'
lj187153 ssh:notty 159.89.207.36 Wed Nov 6 23:50 - 23:50 (00:00)
lft ssh:notty 159.89.207.36 Wed Nov 6 23:50 - 23:50 (00:00)
hsy ssh:notty 159.89.207.36 Wed Nov 6 23:50 - 23:50 (00:00)
hsy ssh:notty 159.89.207.36 Wed Nov 6 23:50 - 23:50 (00:00)
hsy ssh:notty 159.89.207.36 Wed Nov 6 23:50 - 23:50 (00:00)
用for迴圈統計連線數,1小時前的這分鐘登入了6次,上個小時登入了300次
[root@RHEL9 ~]# lastb | awk -v hourago="$(date --date='1 hour ago' '+%a %b %e %H:%M')" '$0~hourago{ip[$3]++}END{for (i in ip) print i,ip[i]}'
159.89.207.36 6
[root@RHEL9 ~]# lastb | awk -v hourago="$(date --date='1 hour ago' '+%a %b %e %H:')" '$0~hourago{ip[$3]++}END{for (i in ip) print i,ip[i]}'
159.89.207.36 300
加入if語句進行條件判斷,連線次數超過200列印出來
[root@RHEL9 ~]# lastb | awk -v hourago="$(date --date='1 hour ago' '+%a %b %e %H:')" '$0~hourago{ip[$3]++}END{for (i in ip){if(ip[i]>200)print i,ip[i]}}'
159.89.207.36 300
[root@RHEL9 ~]# lastb | awk -v hourago="$(date --date='1 hour ago' '+%a %b %e %H:')" '$0~hourago{ip[$3]++}END{for (i in ip){if(ip[i]>400)print i,ip[i]}}'
[root@RHEL9 ~]#
呼叫system{}語句塊,在awk裡執行bash指令並呼叫awk裡的引數,用echo測試下
[root@RHEL9 ~]# lastb | awk -v hourago="$(date --date='1 hour ago' '+%a %b %e %H:')" '$0~hourago{ip[$3]++}END{for (i in ip){if(ip[i]>100){system("echo "i" "ip[i]"")}}}'
159.89.207.36 300
測試防火牆指令,後續還要測試crontab,拉進黑名單就沒法測試了,先拉進白名單
[root@RHEL9 ~]# lastb | awk -v hourago="$(date --date='1 hour ago' '+%a %b %e %H:')" '$0~hourago{ip[$3]++}END{for (i in ip){if(ip[i]>100){system("firewall-cmd --permanent --add-source="i" --zone=trusted;firewall-cmd --reload")}}}'
success
success
[root@RHEL9 ~]# firewall-cmd --get-active-zones
libvirt
interfaces: virbr0
public
interfaces: LANbridge WANbridge enp4s0 DMZbridge
trusted
sources: 159.89.207.36
[root@RHEL9 ~]# firewall-cmd --permanent --remove-source=159.89.207.36 --zone=trusted;firewall-cmd --reload
success
success
編輯郵件內容測試
[root@RHEL9 ~]# lastb | awk -v hourago="$(date --date='1 hour ago' '+%a %b %e %H:')" '$0~hourago{ip[$3]++}END{for (i in ip){if(ip[i]>100){system("firewall-cmd --permanent --remove-source="i" --zone=trusted;firewall-cmd --reload;echo $(hostname) ban "i" for connected "ip[i]" times in 1 hour")}}}'
success
success
RHEL9 ban 159.89.207.36 for connected 300 times in 1 hour
透過mail傳送,echo " 信件內容 "和 mail -s " 信件標題 " 需要用 轉義+引號 " 引起來
[root@RHEL9 ~]# lastb | awk -v hourago="$(date --date='1 hour ago' '+%a %b %e %H:')" '$0~hourago{ip[$3]++}END{for (i in ip){if(ip[i]>100){system("firewall-cmd --permanent --remove-source="i" --zone=block;firewall-cmd --reload;echo \"$(hostname) ban "i" for connected "ip[i]" times in 1 hour\" | mail -s \"Ban Info\" XXXXX@XX.com")}}}'Warning: NOT_ENABLED: 159.89.207.36
success
success
s-nail: Warning: variable superseded or obsoleted: smtp
s-nail: Warning: variable superseded or obsoleted: smtp-auth-user
s-nail: Warning: variable superseded or obsoleted: smtp-auth-password
s-nail: Warning: variable superseded or obsoleted: ssl-verify
s-nail: Obsoletion warning: please do not use *smtp*, instead assign a smtp:// URL to *mta*!
s-nail: Obsoletion warning: Use of old-style credentials, which will vanish in v15!
s-nail: Please read the manual section "On URL syntax and credential lookup"
配置檔案提示語法過時,好歹郵件是過去了
最後查一遍語句寫入crontab
lastb | awk -v hourago="$(date --date='1 hour ago' '+%a %b %e %H:')" '$0~hourago{ip[$3]++}END{for (i in ip){if(ip[i]>11){system("firewall-cmd --permanent --add-source="i" --zone=block;firewall-cmd --reload;echo \"$(hostname) ban "i" for connected "ip[i]" times in 1 hour\" | mail -s \"Ban Info\" xxxx@xx.com")}}}'
上面寫法執行會報錯,只執行到 + 就停了,需要將日期時間格式裡的 % 轉義
之前在centos7上測試時的報錯提示
Message 3:
From root@centos7.localdomain Thu Nov 7 00:05:01 2024
Return-Path: <root@centos7.localdomain>
X-Original-To: root
Delivered-To: root@centos7.localdomain
From: "(Cron Daemon)" <root@centos7.localdomain>
To: root@centos7.localdomain
Subject: Cron <root@centos7> lastb | awk -v hourago="$(date --date='1 hour ago' '+
Content-Type: text/plain; charset=UTF-8
Auto-Submitted: auto-generated
Precedence: bulk
crontab裡的語句
[root@RHEL9 ~]# crontab -l
30 * * * * lastb | awk -v hourago="$(date --date='1 hour ago' '+\%a \%b \%e \%H:')" '$0~hourago{ip[$3]++}END{for (i in ip){if(ip[i]>11){system("firewall-cmd --permanent --add-source="i" --zone=block;firewall-cmd --reload;echo \"$(hostname) ban "i" for connected "ip[i]" times in 1 hour\" | mail -s \"Ban Info\" XXXXX@XX.com")}}}'
防火牆規則
[root@RHEL9 ~]# firewall-cmd --get-active-zones
block
sources: 159.89.207.36
libvirt
interfaces: virbr0
public
interfaces: LANbridge WANbridge enp4s0 DMZbridge
已經不再登入了
[root@RHEL9 ~]# date;lastb | head
Thu Nov 7 02:34:50 AM CST 2024
gaoyuan ssh:notty 159.89.207.36 Thu Nov 7 02:29 - 02:29 (00:00)
gaoyuan ssh:notty 159.89.207.36 Thu Nov 7 02:29 - 02:29 (00:00)
gaojialu ssh:notty 159.89.207.36 Thu Nov 7 02:29 - 02:29 (00:00)
gaojialu ssh:notty 159.89.207.36 Thu Nov 7 02:29 - 02:29 (00:00)
fuyanjie ssh:notty 159.89.207.36 Thu Nov 7 02:29 - 02:29 (00:00)
fuyanjie ssh:notty 159.89.207.36 Thu Nov 7 02:29 - 02:29 (00:00)
fuyahui ssh:notty 159.89.207.36 Thu Nov 7 02:28 - 02:28 (00:00)
cuilingh ssh:notty 159.89.207.36 Thu Nov 7 02:28 - 02:28 (00:00)
cuilingh ssh:notty 159.89.207.36 Thu Nov 7 02:28 - 02:28 (00:00)
cuilingh ssh:notty 159.89.207.36 Thu Nov 7 02:28 - 02:28 (00:00)
郵件
- 最佳化為多IP版:
目前的測試環境是每小時僅檢測出1條IP超出連線次數並拉黑,如果有多條IP滿足拉黑條件,每拉黑1個IP,就要reload1次,然後發1次郵件。例如awk檢索到3條滿足條件的IP,在很短時間內就要reload防火牆3次並連續發3封郵件,簡單粗暴不夠優雅但是也湊合用。考慮上述原因又做了兩點最佳化。
- 修改firewall語句
原先為--permanent寫入配置檔案儲存再--reload讀取並生效
firewall-cmd --permanent --add-source="i" --zone=block;firewall-cmd --reload;
改為先修改規則直接生效然後--runtime-to-permanent再寫入配置檔案儲存,避免了短時間反覆讀取配置檔案
firewall-cmd --add-source="i" --zone=block;firewall-cmd --runtime-to-permanent;
- 將{system}語句塊裡的echo指令顯示的資訊寫入檔案/tmp/baninfo 將mail指令移出{system}語句塊
[root@RHEL9 ~]# lastb | awk -v hourago="$(date --date='22 hour ago' '+%a %b %e %H:')" '$0~hourago{ip[$3]++}END{for (i in ip){if(ip[i]>11){system("firewall-cmd --add-source="i" --zone=block;firewall-cmd --runtime-to-permanent;echo \"$(hostname) ban "i" for connected "ip[i]" times in 1 hour\" >> /tmp/baninfo")}}}';
Warning: ZONE_ALREADY_SET: '159.89.207.36' already bound to 'block'
success
success
[root@RHEL9 ~]# cat /tmp/baninfo
RHEL9 ban 159.89.207.36 for connected 300 times in 1 hour
判斷如果 /tmp/baninfo 存在,傳送郵件,成功後刪除/tmp/baninfo
[ -e /tmp/baninfo ] && cat /tmp/baninfo | mail -s "Ban Info" XXXXX@XX.com && rm -f /tmp/baninfo
- 完整指令
[root@RHEL9 ~]# lastb | awk -v hourago="$(date --date='1 hour ago' '+%a %b %e %H:')" '$0~hourago{ip[$3]++}END{for (i in ip){if(ip[i]>11){system("firewall-cmd --add-source="i" --zone=block;firewall-cmd --runtime-to-permanent;echo \"$(hostname) ban "i" for connected "ip[i]" times in 1 hour\" >> /tmp/baninfo")}}}';[ -e /tmp/baninfo ] && cat /tmp/baninfo | mail -s "Ban Info" xxxxx@xx.com && rm -f /tmp/baninfo
- crontab裡的指令
[root@RHEL9 ~]# crontab -l
0 * * * * lastb | awk -v hourago="$(date --date='1 hour ago' '+\%a \%b \%e \%H:')" '$0~hourago{ip[$3]++}END{for (i in ip){if(ip[i]>11){system("firewall-cmd --add-source="i" --zone=block;firewall-cmd --runtime-to-permanent;echo \"$(hostname) ban "i" for connected "ip[i]" times in 1 hour\" >> /tmp/baninfo")}}}';[ -e /tmp/baninfo ] && cat /tmp/baninfo | mail -s "Ban Info" XXXXX@XX.com && rm -f /tmp/baninfo
- 實現指令碼化自動寫入crontab
[root@RHEL9 ~]# cat ban.sh
#ban.sh
#Date: 2024-11-07
#!/bin/bash
cat << EOF >> /var/spool/cron/root
0 * * * * lastb | awk -v hourago="\$(date --date='1 hour ago' '+\%a \%b \%e \%H:')" '\$0~hourago{ip[\$3]++}END{for (i in ip){if(ip[i]>11){system("firewall-cmd --add-source="i" --zone=block;firewall-cmd --runtime-to-permanent;echo \"\$(hostname) ban "i" for connected "ip[i]" times in 1 hour\" >> /tmp/baninfo")}}}';[ -e /tmp/baninfo ] && cat /tmp/baninfo | mail -s "Ban Info" XXXXX@XX.com && rm -f /tmp/baninfo
EOF
[root@RHEL9 ~]# sh ./ban.sh
[root@RHEL9 ~]# crontab -l
0 * * * * lastb | awk -v hourago="$(date --date='1 hour ago' '+\%a \%b \%e \%H:')" '$0~hourago{ip[$3]++}END{for (i in ip){if(ip[i]>11){system("firewall-cmd --add-source="i" --zone=block;firewall-cmd --runtime-to-permanent;echo \"$(hostname) ban "i" for connected "ip[i]" times in 1 hour\" >> /tmp/baninfo")}}}';[ -e /tmp/baninfo ] && cat /tmp/baninfo | mail -s "Ban Info" XXXXX@XX.com && rm -f /tmp/baninfo
0 * * * * lastb | awk -v hourago="$(date --date='1 hour ago' '+\%a \%b \%e \%H:')" '$0~hourago{ip[$3]++}END{for (i in ip){if(ip[i]>11){system("firewall-cmd --add-source="i" --zone=block;firewall-cmd --runtime-to-permanent;echo \"$(hostname) ban "i" for connected "ip[i]" times in 1 hour\" >> /tmp/baninfo")}}}';[ -e /tmp/baninfo ] && cat /tmp/baninfo | mail -s "Ban Info" XXXXX@XX.com && rm -f /tmp/baninfo
- 注意事項
RHEL9最小化安裝沒有mail指令,發件前請確認主機安裝mail - 不足之處
透過mail傳送需要設定.mailrc檔案,QQ郵箱需要加密我沒設定好,163郵箱不需加密目前我在用,但是認證碼半年到期就要換一次。我用的.mailrc配置檔案在CentOS7上正常,在RHEL9上提示快過時了,需要用新的語法,設定.mailrc的方法暫時不寫了,有時間再好好研究。