Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
eterban
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
etersoft
eterban
Commits
59d6cb2e
Commit
59d6cb2e
authored
Jan 02, 2026
by
Vitaly Lipatov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add auto-unban with exponential backoff
parent
c5d64d07
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
581 additions
and
11 deletions
+581
-11
settings.ini
common/etc/eterban/settings.ini
+24
-0
eterban.sh
gateway/usr/bin/eterban.sh
+35
-8
autoban_cli.py
gateway/usr/share/eterban/autoban_cli.py
+211
-0
autoban_manager.py
gateway/usr/share/eterban/autoban_manager.py
+244
-0
eterban_switcher.py
gateway/usr/share/eterban/eterban_switcher.py
+67
-3
No files found.
common/etc/eterban/settings.ini
View file @
59d6cb2e
...
@@ -12,3 +12,27 @@
...
@@ -12,3 +12,27 @@
#i_interface2 = brlocal2
#i_interface2 = brlocal2
#hostname = localhost
#hostname = localhost
[AutoUnban]
# Enable automatic unbanning with exponential backoff
enabled
=
true
# Base ban duration for first offense (seconds)
# Default: 3600 (1 hour)
base_ban_seconds
=
3600
# Backoff multiplier for each subsequent offense
# Duration = base_ban_seconds * (backoff_multiplier ^ (offense_count - 1))
# With multiplier=4: 1h -> 4h -> 16h -> 64h -> permanent
backoff_multiplier
=
4
# Maximum offense level before permanent ban
# offense_count > max_offense_level = permanent ban
max_offense_level
=
4
# Period after which offense counter resets (days)
# If IP is clean for this period, counter resets to 0
reset_period_days
=
365
# How often to check for expired bans (seconds)
check_interval_seconds
=
60
gateway/usr/bin/eterban.sh
View file @
59d6cb2e
...
@@ -43,14 +43,41 @@ if [ "$command" = "clear" ] ; then
...
@@ -43,14 +43,41 @@ if [ "$command" = "clear" ] ; then
exit
exit
fi
fi
if
[
"
$command
"
=
"info"
]
;
then
/usr/share/eterban/autoban_cli.py info
$1
exit
fi
if
[
"
$command
"
=
"reset"
]
;
then
/usr/share/eterban/autoban_cli.py reset
$1
exit
fi
if
[
"
$command
"
=
"pending"
]
;
then
/usr/share/eterban/autoban_cli.py pending
exit
fi
if
[
"
$command
"
=
"permanent"
]
;
then
/usr/share/eterban/autoban_cli.py permanent
exit
fi
cat
<<
EOF
cat
<<
EOF
Usage:
Usage:
eterban [count|list|search <ip>]
eterban <command> [args]
count - print count of banned IPs
Commands:
list - list all banned IPs
count - print count of banned IPs
search <ip> - search for ip in the list of banned IPs
list - list all banned IPs
unban <ip> - unban IP
search <ip> - search for ip in the list of banned IPs
ban <ip> - ban IP
unban <ip> - unban IP
clear - remove all IPs from ban
ban <ip> - ban IP
clear - remove all IPs from ban
Auto-unban commands:
info <ip> - show ban info and history for IP
reset <ip> - reset offense counter for IP
pending - list pending auto-unbans
permanent - list permanent bans
EOF
EOF
gateway/usr/share/eterban/autoban_cli.py
0 → 100644
View file @
59d6cb2e
#!/usr/bin/python3
"""
CLI для команд автоматической разблокировки eterban.
Команды:
info <ip> - показать информацию о бане и историю
reset <ip> - сбросить счётчик нарушений
pending - показать запланированные авто-разбаны
permanent - показать постоянные баны
"""
import
sys
import
time
import
configparser
import
redis
# Пути
CONFIG_PATH
=
'/etc/eterban/settings.ini'
# Ключи Redis
META_PREFIX
=
'eterban:meta:'
SCHEDULE_KEY
=
'eterban:unban_schedule'
PERMANENT_KEY
=
'eterban:permanent'
def
format_duration
(
seconds
):
"""Форматировать длительность в человекочитаемый вид."""
if
seconds
<=
0
:
return
"permanent"
hours
=
seconds
//
3600
minutes
=
(
seconds
%
3600
)
//
60
if
hours
>
0
:
return
f
"{hours}h {minutes}m"
return
f
"{minutes}m"
def
format_time
(
timestamp
):
"""Форматировать Unix timestamp."""
if
timestamp
<=
0
:
return
"permanent"
return
time
.
strftime
(
'
%
Y-
%
m-
%
d
%
H:
%
M:
%
S'
,
time
.
localtime
(
timestamp
))
def
get_redis
():
"""Получить подключение к Redis."""
config
=
configparser
.
ConfigParser
()
config
.
read
(
CONFIG_PATH
)
redis_server
=
config
.
get
(
'Settings'
,
'redis_server'
,
fallback
=
'localhost'
)
return
redis
.
Redis
(
host
=
redis_server
)
def
cmd_info
(
ip
):
"""Показать информацию о бане IP."""
r
=
get_redis
()
meta
=
r
.
hgetall
(
f
"{META_PREFIX}{ip}"
)
if
not
meta
:
print
(
f
"No ban history for {ip}"
)
return
# Декодируем значения
data
=
{
k
.
decode
():
v
.
decode
()
for
k
,
v
in
meta
.
items
()}
offense_count
=
int
(
data
.
get
(
'offense_count'
,
0
))
ban_time
=
int
(
data
.
get
(
'ban_time'
,
0
))
unban_time
=
int
(
data
.
get
(
'unban_time'
,
0
))
last_offense
=
int
(
data
.
get
(
'last_offense'
,
0
))
source
=
data
.
get
(
'source'
,
'unknown'
)
reason
=
data
.
get
(
'reason'
,
'unknown'
)
# Рассчитываем длительность
if
unban_time
>
0
:
duration
=
unban_time
-
ban_time
status
=
"temporary"
remaining
=
unban_time
-
int
(
time
.
time
())
if
remaining
<
0
:
remaining_str
=
"expired (waiting for unban)"
else
:
remaining_str
=
format_duration
(
remaining
)
else
:
duration
=
0
status
=
"permanent"
remaining_str
=
"permanent"
# Проверяем, в каком списке
is_scheduled
=
r
.
zscore
(
SCHEDULE_KEY
,
ip
)
is
not
None
is_permanent
=
r
.
sismember
(
PERMANENT_KEY
,
ip
)
print
(
f
"Ban info for {ip}:"
)
print
(
f
" Offense count: {offense_count}"
)
print
(
f
" Status: {status}"
)
print
(
f
" Ban duration: {format_duration(duration)}"
)
print
(
f
" Remaining: {remaining_str}"
)
print
(
f
" Ban time: {format_time(ban_time)}"
)
print
(
f
" Unban time: {format_time(unban_time)}"
)
print
(
f
" Last offense: {format_time(last_offense)}"
)
print
(
f
" Source: {source}"
)
print
(
f
" Reason: {reason}"
)
print
(
f
" In schedule: {'yes' if is_scheduled else 'no'}"
)
print
(
f
" In permanent: {'yes' if is_permanent else 'no'}"
)
def
cmd_reset
(
ip
):
"""Сбросить счётчик нарушений для IP."""
r
=
get_redis
()
meta_key
=
f
"{META_PREFIX}{ip}"
if
not
r
.
exists
(
meta_key
):
print
(
f
"No ban history for {ip}"
)
return
r
.
delete
(
meta_key
)
r
.
zrem
(
SCHEDULE_KEY
,
ip
)
r
.
srem
(
PERMANENT_KEY
,
ip
)
print
(
f
"Reset offense counter for {ip}"
)
def
cmd_pending
():
"""Показать запланированные авто-разбаны."""
r
=
get_redis
()
pending
=
r
.
zrangebyscore
(
SCHEDULE_KEY
,
0
,
'+inf'
,
withscores
=
True
)
if
not
pending
:
print
(
"No pending auto-unbans"
)
return
now
=
int
(
time
.
time
())
print
(
f
"Pending auto-unbans ({len(pending)} total):"
)
print
()
for
ip
,
unban_time
in
pending
:
ip
=
ip
.
decode
()
if
isinstance
(
ip
,
bytes
)
else
ip
unban_time
=
int
(
unban_time
)
remaining
=
unban_time
-
now
if
remaining
<
0
:
remaining_str
=
"EXPIRED"
else
:
remaining_str
=
format_duration
(
remaining
)
print
(
f
" {ip:20s} unban at {format_time(unban_time)} ({remaining_str})"
)
def
cmd_permanent
():
"""Показать постоянные баны."""
r
=
get_redis
()
members
=
r
.
smembers
(
PERMANENT_KEY
)
if
not
members
:
print
(
"No permanent bans"
)
return
print
(
f
"Permanent bans ({len(members)} total):"
)
print
()
for
ip
in
sorted
(
members
):
ip
=
ip
.
decode
()
if
isinstance
(
ip
,
bytes
)
else
ip
# Получаем дополнительную информацию
meta
=
r
.
hgetall
(
f
"{META_PREFIX}{ip}"
)
if
meta
:
data
=
{
k
.
decode
():
v
.
decode
()
for
k
,
v
in
meta
.
items
()}
offense_count
=
data
.
get
(
'offense_count'
,
'?'
)
source
=
data
.
get
(
'source'
,
'unknown'
)
print
(
f
" {ip:20s} offenses: {offense_count} source: {source}"
)
else
:
print
(
f
" {ip}"
)
def
usage
():
print
(
"""Usage: autoban_cli.py <command> [args]
Commands:
info <ip> - show ban info and history for IP
reset <ip> - reset offense counter for IP
pending - list pending auto-unbans
permanent - list permanent bans
"""
)
sys
.
exit
(
1
)
def
main
():
if
len
(
sys
.
argv
)
<
2
:
usage
()
cmd
=
sys
.
argv
[
1
]
if
cmd
==
'info'
:
if
len
(
sys
.
argv
)
<
3
:
print
(
"Error: IP address required"
)
sys
.
exit
(
1
)
cmd_info
(
sys
.
argv
[
2
])
elif
cmd
==
'reset'
:
if
len
(
sys
.
argv
)
<
3
:
print
(
"Error: IP address required"
)
sys
.
exit
(
1
)
cmd_reset
(
sys
.
argv
[
2
])
elif
cmd
==
'pending'
:
cmd_pending
()
elif
cmd
==
'permanent'
:
cmd_permanent
()
else
:
print
(
f
"Unknown command: {cmd}"
)
usage
()
if
__name__
==
'__main__'
:
main
()
gateway/usr/share/eterban/autoban_manager.py
0 → 100644
View file @
59d6cb2e
#!/usr/bin/python3
"""
AutoBanManager - управление автоматической разблокировкой с экспоненциальным увеличением времени бана.
Логика:
- Первый бан: 1 час
- Второй бан: 4 часа (4^1)
- Третий бан: 16 часов (4^2)
- Четвёртый бан: 64 часа (4^3)
- Пятый+ бан: постоянный
Если IP чист 1 год - счётчик сбрасывается.
"""
import
time
import
logging
log
=
logging
.
getLogger
(
__name__
)
class
AutoBanManager
:
"""Менеджер автоматической разблокировки с экспоненциальным backoff."""
def
__init__
(
self
,
redis_client
,
config
):
"""
Инициализация менеджера.
Args:
redis_client: Подключение к Redis
config: ConfigParser с секцией [AutoUnban]
"""
self
.
r
=
redis_client
# Читаем конфигурацию с fallback значениями
self
.
enabled
=
config
.
getboolean
(
'AutoUnban'
,
'enabled'
,
fallback
=
True
)
self
.
base_ban_seconds
=
config
.
getint
(
'AutoUnban'
,
'base_ban_seconds'
,
fallback
=
3600
)
self
.
backoff_multiplier
=
config
.
getint
(
'AutoUnban'
,
'backoff_multiplier'
,
fallback
=
4
)
self
.
max_offense_level
=
config
.
getint
(
'AutoUnban'
,
'max_offense_level'
,
fallback
=
4
)
self
.
reset_period_seconds
=
config
.
getint
(
'AutoUnban'
,
'reset_period_days'
,
fallback
=
365
)
*
86400
self
.
check_interval
=
config
.
getint
(
'AutoUnban'
,
'check_interval_seconds'
,
fallback
=
60
)
# Ключи Redis
self
.
META_PREFIX
=
'eterban:meta:'
self
.
SCHEDULE_KEY
=
'eterban:unban_schedule'
self
.
PERMANENT_KEY
=
'eterban:permanent'
def
calculate_ban_duration
(
self
,
offense_count
):
"""
Рассчитать длительность бана.
Args:
offense_count: Номер нарушения (1, 2, 3, ...)
Returns:
Длительность в секундах, или 0 для постоянного бана
"""
if
offense_count
>
self
.
max_offense_level
:
return
0
# Permanent
return
self
.
base_ban_seconds
*
(
self
.
backoff_multiplier
**
(
offense_count
-
1
))
def
on_ban
(
self
,
ip
,
source
=
'unknown'
,
reason
=
'auto'
):
"""
Обработка нового бана.
Args:
ip: IP-адрес
source: Источник бана (hostname сервера)
reason: Причина бана (SSH, HTTP, etc.)
Returns:
dict с метаданными бана
"""
if
not
self
.
enabled
:
return
None
try
:
meta_key
=
f
"{self.META_PREFIX}{ip}"
now
=
int
(
time
.
time
())
# Получаем существующие метаданные
existing
=
self
.
r
.
hgetall
(
meta_key
)
if
existing
:
offense_count
=
int
(
existing
.
get
(
b
'offense_count'
,
0
))
last_offense
=
int
(
existing
.
get
(
b
'last_offense'
,
0
))
# Проверяем, прошёл ли период сброса (1 год)
if
now
-
last_offense
>
self
.
reset_period_seconds
:
offense_count
=
0
offense_count
+=
1
else
:
offense_count
=
1
# Рассчитываем время бана
ban_duration
=
self
.
calculate_ban_duration
(
offense_count
)
unban_time
=
now
+
ban_duration
if
ban_duration
>
0
else
0
# Сохраняем метаданные
metadata
=
{
'offense_count'
:
offense_count
,
'ban_time'
:
now
,
'unban_time'
:
unban_time
,
'last_offense'
:
now
,
'source'
:
source
,
'reason'
:
reason
}
self
.
r
.
hset
(
meta_key
,
mapping
=
metadata
)
# Устанавливаем TTL для очистки (1 год после последнего нарушения)
self
.
r
.
expire
(
meta_key
,
self
.
reset_period_seconds
)
# Добавляем в расписание авто-разбана или в постоянные
if
unban_time
>
0
:
self
.
r
.
zadd
(
self
.
SCHEDULE_KEY
,
{
ip
:
unban_time
})
self
.
r
.
srem
(
self
.
PERMANENT_KEY
,
ip
)
# На случай если был permanent
else
:
self
.
r
.
sadd
(
self
.
PERMANENT_KEY
,
ip
)
self
.
r
.
zrem
(
self
.
SCHEDULE_KEY
,
ip
)
# Убираем из расписания
return
metadata
except
Exception
as
e
:
log
.
error
(
f
"AutoBanManager.on_ban error: {e}"
)
return
None
def
on_unban
(
self
,
ip
,
reset_counter
=
False
):
"""
Обработка разблокировки.
Args:
ip: IP-адрес
reset_counter: Сбросить счётчик нарушений
"""
if
not
self
.
enabled
:
return
try
:
# Удаляем из расписания и постоянных
self
.
r
.
zrem
(
self
.
SCHEDULE_KEY
,
ip
)
self
.
r
.
srem
(
self
.
PERMANENT_KEY
,
ip
)
if
reset_counter
:
self
.
r
.
delete
(
f
"{self.META_PREFIX}{ip}"
)
except
Exception
as
e
:
log
.
error
(
f
"AutoBanManager.on_unban error: {e}"
)
def
get_expired_bans
(
self
):
"""
Получить список IP с истёкшим временем бана.
Returns:
Список IP-адресов для разблокировки
"""
if
not
self
.
enabled
:
return
[]
try
:
now
=
int
(
time
.
time
())
expired
=
self
.
r
.
zrangebyscore
(
self
.
SCHEDULE_KEY
,
0
,
now
)
return
[
ip
.
decode
()
if
isinstance
(
ip
,
bytes
)
else
ip
for
ip
in
expired
]
except
Exception
as
e
:
log
.
error
(
f
"AutoBanManager.get_expired_bans error: {e}"
)
return
[]
def
remove_from_schedule
(
self
,
ip
):
"""Удалить IP из расписания после авто-разбана."""
try
:
self
.
r
.
zrem
(
self
.
SCHEDULE_KEY
,
ip
)
except
Exception
as
e
:
log
.
error
(
f
"AutoBanManager.remove_from_schedule error: {e}"
)
def
get_ban_info
(
self
,
ip
):
"""
Получить информацию о бане для CLI.
Args:
ip: IP-адрес
Returns:
dict с метаданными или None
"""
try
:
meta
=
self
.
r
.
hgetall
(
f
"{self.META_PREFIX}{ip}"
)
if
not
meta
:
return
None
return
{
k
.
decode
():
v
.
decode
()
for
k
,
v
in
meta
.
items
()}
except
Exception
as
e
:
log
.
error
(
f
"AutoBanManager.get_ban_info error: {e}"
)
return
None
def
get_pending_unbans
(
self
):
"""
Получить список запланированных разбанов.
Returns:
Список кортежей (ip, unban_time)
"""
try
:
pending
=
self
.
r
.
zrangebyscore
(
self
.
SCHEDULE_KEY
,
0
,
'+inf'
,
withscores
=
True
)
return
[(
ip
.
decode
()
if
isinstance
(
ip
,
bytes
)
else
ip
,
int
(
score
))
for
ip
,
score
in
pending
]
except
Exception
as
e
:
log
.
error
(
f
"AutoBanManager.get_pending_unbans error: {e}"
)
return
[]
def
get_permanent_bans
(
self
):
"""
Получить список постоянных банов.
Returns:
Список IP-адресов
"""
try
:
members
=
self
.
r
.
smembers
(
self
.
PERMANENT_KEY
)
return
[
ip
.
decode
()
if
isinstance
(
ip
,
bytes
)
else
ip
for
ip
in
members
]
except
Exception
as
e
:
log
.
error
(
f
"AutoBanManager.get_permanent_bans error: {e}"
)
return
[]
def
reset_offense_counter
(
self
,
ip
):
"""
Сбросить счётчик нарушений для IP.
Args:
ip: IP-адрес
"""
try
:
self
.
r
.
delete
(
f
"{self.META_PREFIX}{ip}"
)
self
.
r
.
zrem
(
self
.
SCHEDULE_KEY
,
ip
)
self
.
r
.
srem
(
self
.
PERMANENT_KEY
,
ip
)
except
Exception
as
e
:
log
.
error
(
f
"AutoBanManager.reset_offense_counter error: {e}"
)
def
format_duration
(
self
,
seconds
):
"""Форматировать длительность в человекочитаемый вид."""
if
seconds
<=
0
:
return
"permanent"
hours
=
seconds
//
3600
minutes
=
(
seconds
%
3600
)
//
60
if
hours
>
0
:
return
f
"{hours}h {minutes}m"
return
f
"{minutes}m"
gateway/usr/share/eterban/eterban_switcher.py
View file @
59d6cb2e
...
@@ -8,6 +8,9 @@ import os
...
@@ -8,6 +8,9 @@ import os
import
signal
import
signal
import
socket
import
socket
import
ipaddress
import
ipaddress
import
threading
import
re
from
autoban_manager
import
AutoBanManager
path_to_config
=
'/etc/eterban/settings.ini'
path_to_config
=
'/etc/eterban/settings.ini'
path_to_eterban
=
'/usr/share/eterban/'
path_to_eterban
=
'/usr/share/eterban/'
...
@@ -229,6 +232,40 @@ except:
...
@@ -229,6 +232,40 @@ except:
print
(
"Unable to connect redis"
)
print
(
"Unable to connect redis"
)
sys
.
exit
(
1
)
sys
.
exit
(
1
)
# Инициализация AutoBanManager
config
=
configparser
.
ConfigParser
()
config
.
read
(
path_to_config
)
auto_mgr
=
AutoBanManager
(
r
,
config
)
def
auto_unban_checker
():
"""Фоновый поток для проверки и выполнения авто-разбанов."""
while
True
:
time
.
sleep
(
auto_mgr
.
check_interval
)
if
not
auto_mgr
.
enabled
:
continue
try
:
expired
=
auto_mgr
.
get_expired_bans
()
for
ip
in
expired
:
# Публикуем разбан
r
.
publish
(
'unban'
,
ip
)
r
.
publish
(
'by'
,
f
"{ip} auto-unbanned after ban period expired"
)
auto_mgr
.
remove_from_schedule
(
ip
)
info
=
time
.
strftime
(
"
%
Y-
%
m-
%
d
%
H:
%
M:
%
S"
,
time
.
localtime
())
info
+=
f
" {ip} auto-unbanned
\n
"
log
.
write
(
info
)
log
.
flush
()
except
Exception
as
e
:
info
=
time
.
strftime
(
"
%
Y-
%
m-
%
d
%
H:
%
M:
%
S"
,
time
.
localtime
())
info
+=
f
" auto_unban_checker error: {e}
\n
"
log
.
write
(
info
)
log
.
flush
()
# Запуск фонового потока
if
auto_mgr
.
enabled
:
checker_thread
=
threading
.
Thread
(
target
=
auto_unban_checker
,
daemon
=
True
)
checker_thread
.
start
()
restore_ipset_eterban_1
()
restore_ipset_eterban_1
()
create_iptables_rules
()
create_iptables_rules
()
create_ip6tables_rules
()
create_ip6tables_rules
()
...
@@ -253,22 +290,49 @@ for message in p.listen():
...
@@ -253,22 +290,49 @@ for message in p.listen():
elif
message
is
not
None
and
message
[
'type'
]
==
'message'
and
message
[
'channel'
]
==
b
'unban'
:
elif
message
is
not
None
and
message
[
'type'
]
==
'message'
and
message
[
'channel'
]
==
b
'unban'
:
print
(
message
)
print
(
message
)
ip
=
message
[
'data'
]
.
decode
(
'utf-8'
)
ip
=
message
[
'data'
]
.
decode
(
'utf-8'
)
ipo
=
ipaddress
.
ip_
address
(
ip
)
ipo
=
ipaddress
.
ip_
network
(
ip
,
strict
=
False
)
if
isinstance
(
ipo
,
ipaddress
.
IPv6Address
):
if
isinstance
(
ipo
,
ipaddress
.
IPv6Address
):
unban
=
'ipset -D '
+
ipset_eterban_1_ipv6
+
' '
+
ip
unban
=
'ipset -D '
+
ipset_eterban_1_ipv6
+
' '
+
ip
el
se
:
el
if
isinstance
(
ipo
,
ipaddress
.
IPv4Network
)
:
unban
=
'ipset -D '
+
ipset_eterban_1
+
' '
+
ip
unban
=
'ipset -D '
+
ipset_eterban_1
+
' '
+
ip
else
:
log
.
write
(
"Not parsed as IP, skipped "
+
str
(
ip
)
+
'
\n
'
)
#add = 'ipset -A ' + ipset_eterban_white + ' ' + ip
#add = 'ipset -A ' + ipset_eterban_white + ' ' + ip
subprocess
.
call
(
unban
,
stdout
=
subprocess
.
PIPE
,
stderr
=
subprocess
.
PIPE
,
shell
=
True
)
subprocess
.
call
(
unban
,
stdout
=
subprocess
.
PIPE
,
stderr
=
subprocess
.
PIPE
,
shell
=
True
)
#subprocess.call (add, shell = True)
#subprocess.call (add, shell = True)
tcp_drop
=
'conntrack -D -s '
+
ip
tcp_drop
=
'conntrack -D -s '
+
ip
subprocess
.
Popen
(
tcp_drop
,
stdout
=
subprocess
.
PIPE
,
stderr
=
subprocess
.
PIPE
,
shell
=
True
)
subprocess
.
Popen
(
tcp_drop
,
stdout
=
subprocess
.
PIPE
,
stderr
=
subprocess
.
PIPE
,
shell
=
True
)
# AutoBan: обновляем метаданные (убираем из расписания)
if
auto_mgr
.
enabled
:
auto_mgr
.
on_unban
(
ip
)
elif
message
is
not
None
and
message
[
'type'
]
==
'message'
and
message
[
'channel'
]
==
b
'by'
:
elif
message
is
not
None
and
message
[
'type'
]
==
'message'
and
message
[
'channel'
]
==
b
'by'
:
by_msg
=
message
[
'data'
]
.
decode
(
'utf-8'
)
info
=
time
.
strftime
(
"
%
Y-
%
m-
%
d
%
H:
%
M:
%
S"
,
time
.
localtime
())
info
=
time
.
strftime
(
"
%
Y-
%
m-
%
d
%
H:
%
M:
%
S"
,
time
.
localtime
())
info
+=
" "
+
message
[
'data'
]
.
decode
(
'utf-8'
)
+
"
\n
"
info
+=
" "
+
by_msg
+
"
\n
"
print
(
info
)
print
(
info
)
log
.
write
(
info
)
log
.
write
(
info
)
log
.
flush
()
log
.
flush
()
# AutoBan: парсим сообщение и сохраняем метаданные
# Формат: "IP was blocked by HOSTNAME: REASON" или "IP was blocked by HOSTNAME (...)"
if
auto_mgr
.
enabled
:
match
=
re
.
match
(
r'^(\S+) was blocked by ([^:]+)(?:: (.+))?$'
,
by_msg
)
if
match
:
ip
=
match
.
group
(
1
)
source
=
match
.
group
(
2
)
.
strip
()
reason
=
match
.
group
(
3
)
if
match
.
group
(
3
)
else
'auto'
meta
=
auto_mgr
.
on_ban
(
ip
,
source
=
source
,
reason
=
reason
)
if
meta
:
ban_duration
=
meta
.
get
(
'unban_time'
,
0
)
-
int
(
time
.
time
())
offense
=
meta
.
get
(
'offense_count'
,
1
)
if
ban_duration
>
0
:
auto_info
=
f
" -> offense #{offense}, auto-unban in {auto_mgr.format_duration(ban_duration)}
\n
"
else
:
auto_info
=
f
" -> offense #{offense}, PERMANENT ban
\n
"
print
(
auto_info
)
log
.
write
(
auto_info
)
log
.
flush
()
elif
message
is
not
None
:
elif
message
is
not
None
:
print
(
"AHTUNG!!1!"
,
message
)
print
(
"AHTUNG!!1!"
,
message
)
info
=
time
.
strftime
(
"
%
Y-
%
m-
%
d
%
H:
%
M:
%
S"
,
time
.
localtime
())
info
=
time
.
strftime
(
"
%
Y-
%
m-
%
d
%
H:
%
M:
%
S"
,
time
.
localtime
())
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment