前言
通过frp进行穿透后,ssh客户机无法感知真实的ip地址,只显示部署frpc的本地ip。所以当有人爆破我们的服务器时,无法通过客户机进行ip封禁操作。此脚本部署在frps服务器端。
1. 环境说明
系统:Debian11
防火墙:iptables
脚本环境:python3.9
frps版本:frp_0.53.2_linux_amd64
2.原理
根据frps生成的日志文件,检测连接ssh服务的ip。利用python脚本读取每一行,匹配ssh关键字。同一个IP在一段时间内多次连接ssh服务则封禁。同时将ip写进blocked_ips.txt。每次执行脚本时会读取txt检测该ip是否早已添加,防止重复添加。然后清除防火墙INPUT规则,将txt里的ip记录重新添加到防火墙进行拦截。
2024.07.15更新--IP属地检测,因爆破用的肉鸡多为国外机器,所以只封禁国外IP。
3.实现步骤
1.frps开启日志
在frps.toml配置文件里添加:
# console or real logFile path like ./frps.log
log.to = "/root/frp/frp_0.53.2_linux_amd64/frps.log"
# trace, debug, info, warn, error
log.level = "info"
#log.level = "debug"
log.maxDays = 3
# disable log colors when log.to is console, default is false
#log.disablePrintColor = false
2.编写python脚本
参数说明:
/root/frp/frp_0.53.2_linux_amd64/blocked_ips.log 为脚本的操作日志
/root/frp/frp_0.53.2_linux_amd64/frps.log 为frps的日志
/root/frp/frp_0.53.2_linux_amd64/blocked_ips.txt 为封禁ip的合集
这两行代表10分钟内5次爆破即满足封禁条件
time_interval = timedelta(minutes=10)
if info['count'] >= 5:
请按需修改。
import subprocess
from datetime import datetime, timedelta
import re
import logging
import requests
# 配置日志
logging.basicConfig(filename='/root/frp/frp_0.53.2_linux_amd64/blocked_ips.log', level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s')
# 定义时间间隔为20分钟
time_interval = timedelta(minutes=20)
# 创建一个字典用于存储IP地址和其出现次数
ip_counts = {}
# 定义一个函数获取 IP 属地信息
def get_ip_location(ip):
try:
response = requests.get(f'https://ipinfo.io/widget/demo/{ip}')
if response.status_code == 200:
data = response.json().get('data', {})
nation = data.get('country', '')
return nation
else:
logging.warning(f"Failed to get location for IP {ip}. Status code: {response.status_code}")
return ''
except Exception as e:
logging.error(f"Error occurred while getting location for IP {ip}: {str(e)}")
return ''
# 读取日志文件
with open('/root/frp/frp_0.53.2_linux_amd64/frps.log', 'r') as file:
for line in file:
# 使用正则表达式匹配时间戳和IP地址
match = re.search(
r'(\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}) .* \[.*ssh.*\] get a user connection \[([\d.]+):\d+\]', line)
if match:
timestamp_str, ip = match.group(1), match.group(2)
timestamp = datetime.strptime(timestamp_str, '%Y/%m/%d %H:%M:%S')
# 检查当前行的IP地址是否已经在字典中
if ip in ip_counts:
# 检查时间间隔是否在20分钟内
if timestamp - ip_counts[ip]['timestamp'] <= time_interval:
ip_counts[ip]['count'] += 1
else:
ip_counts[ip] = {'timestamp': timestamp, 'count': 1}
else:
ip_counts[ip] = {'timestamp': timestamp, 'count': 1}
# 输出IP地址和其出现次数到txt文本,并封禁IP地址并记录到日志
blocked_ips_set = set()
with open('/root/frp/frp_0.53.2_linux_amd64/blocked_ips.txt', 'a+') as output_file:
output_file.seek(0) # 移动到文件开头
existing_ips = output_file.read().splitlines()
blocked_ips_set.update(existing_ips) # 将已存在的 IP 加入集合中,避免重复写入
for ip, info in ip_counts.items():
if info['count'] >= 5 and ip not in blocked_ips_set:
# 获取 IP 属地信息
nation = get_ip_location(ip)
if nation != 'CN' and nation != '':
message = f'IP: {ip}, Count: {info["count"]}, Timestamp: {info["timestamp"]}, Nation: {nation}, has been recorded in blocked_ips.txt.'
print(message)
logging.info(message)
# 将符合条件的 IP 写入文本
output_file.write(ip + '\n')
blocked_ips_set.add(ip)
# 读取txt文本内的IP并利用iptables封禁
with open('/root/frp/frp_0.53.2_linux_amd64/blocked_ips.txt', 'r') as input_file:
blocked_ips = input_file.read().splitlines()
for ip in blocked_ips:
subprocess.run(['/usr/sbin/iptables', '-I', 'INPUT', '-s', ip, '-j', 'DROP'])
message = 'All IPs in blocked_ips.txt have been blocked with iptables.'
print(message)
logging.info(message)
3.定时执行脚本
利用cron定时执行脚本:例如10min
crontab -e
*/10 * * * * /usr/bin/python3 /root/frp/frp_0.53.2_linux_amd64/ban_frps_ip.py
注意python路径,以及脚本路径。
4.日志分割
可以参考这篇文章进行日志分割,将blocked_ips.log按天分割
4.注意事项
frp客户端在进行配置时需要把name = ""添加上ssh关键字。例如:
[[proxies]]
name = "Ubuntu-ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = 122
并且重启后封禁将失效。如需永久生效需要添加额外的iptables命令。
效果截图:


Comments NOTHING