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
a9efc009
Commit
a9efc009
authored
Jul 20, 2026
by
Vitaly Lipatov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
gateway: restore active bans from Redis
parent
090ca570
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
108 additions
and
15 deletions
+108
-15
settings.ini
common/etc/eterban/settings.ini
+2
-0
eterban_switcher.py
gateway/usr/share/eterban/eterban_switcher.py
+106
-15
No files found.
common/etc/eterban/settings.ini
View file @
a9efc009
[Settings]
# blocking requests queue
#redis_server = 10.20.30.101
# Redis stores the authoritative active-ban set. Configure the Redis server
# with durable persistence (for example: appendonly yes, appendfsync everysec).
# Redirect banned to
#ban_server = 192.168.0.0
...
...
gateway/usr/share/eterban/eterban_switcher.py
View file @
a9efc009
...
...
@@ -13,12 +13,13 @@ import re
from
autoban_manager
import
AutoBanManager
path_to_config
=
'/etc/eterban/settings.ini'
path_to_eterban
=
'/usr/share/eterban/'
ipset_eterban_1
=
'eterban_1'
ipset_eterban_1_ipv6
=
'eterban_1_ipv6'
ipset_firehol
=
'firehol_level1'
ipset_eterban_white
=
'eterban_white'
ipset_eterban_white_ipv6
=
'eterban_white_ipv6'
redis_bans_key
=
'eterban:active_bans'
redis_bans_initialized_key
=
'eterban:active_bans:initialized'
try
:
path_to_log
=
'/var/log/eterban/eterban.log'
...
...
@@ -80,19 +81,12 @@ def parse_config (path_to_config, path_to_log):
maxelem
=
config
.
getint
(
"Settings"
,
"maxelem"
,
fallback
=
2000000
)
return
(
redis_server
,
ban_server
,
ban_server_ipv6
,
wan_ifaces
,
internal_interface
,
maxelem
,
whitelist_file
)
def
save_ipset_eterban_1
():
global
ipset_eterban_1
,
ipset_eterban_1_ipv6
,
ipset_firehol
,
path_to_eterban
# whitelist is not saved: it is rebuilt from whitelist_file on startup
def
restore_legacy_ipsets
():
"""One-time migration fallback for snapshots made by older releases."""
global
ipset_eterban_1
,
ipset_eterban_1_ipv6
,
ipset_firehol
name_list
=
[
ipset_eterban_1
,
ipset_eterban_1_ipv6
,
ipset_firehol
]
for
name
in
name_list
:
command
=
'ipset save '
+
name
+
' --file '
+
path_to_eterban
+
name
subprocess
.
call
(
command
,
shell
=
True
)
def
restore_ipset_eterban_1
():
global
ipset_eterban_1
,
ipset_eterban_1_ipv6
,
ipset_firehol
,
path_to_eterban
name_list
=
[
ipset_eterban_1
,
ipset_eterban_1_ipv6
,
ipset_firehol
]
for
name
in
name_list
:
command
=
'ipset restore --file '
+
path_to_eterban
+
name
command
=
'ipset restore --file /usr/share/eterban/'
+
name
subprocess
.
call
(
command
,
shell
=
True
)
...
...
@@ -264,7 +258,6 @@ def destroy_ip6tables_rules ():
def
exit_gracefully
(
signum
,
frame
):
save_ipset_eterban_1
()
destroy_iptables_rules
()
destroy_ip6tables_rules
()
...
...
@@ -293,6 +286,93 @@ def log_redis_error(message):
log
.
flush
()
def
get_ipset_members
(
setname
):
"""Return valid address members from an ipset without parsing human output."""
try
:
result
=
subprocess
.
run
(
[
'ipset'
,
'save'
,
setname
],
text
=
True
,
capture_output
=
True
,
check
=
True
,
timeout
=
10
)
except
(
OSError
,
subprocess
.
SubprocessError
)
as
error
:
log_redis_error
(
"Unable to read "
+
setname
+
" for Redis migration: "
+
str
(
error
))
return
[]
members
=
[]
for
line
in
result
.
stdout
.
splitlines
():
fields
=
line
.
split
()
if
len
(
fields
)
<
3
or
fields
[
0
]
!=
'add'
or
fields
[
1
]
!=
setname
:
continue
try
:
members
.
append
(
str
(
ipaddress
.
ip_address
(
fields
[
2
])))
except
ValueError
:
log_redis_error
(
"Ignoring invalid legacy ipset entry: "
+
fields
[
2
])
return
members
def
initialize_ban_state
():
"""Import the old ipset snapshot once, then make Redis authoritative."""
try
:
if
r
.
exists
(
redis_bans_initialized_key
):
return
members
=
get_ipset_members
(
ipset_eterban_1
)
members
.
extend
(
get_ipset_members
(
ipset_eterban_1_ipv6
))
pipeline
=
r
.
pipeline
()
if
members
:
pipeline
.
sadd
(
redis_bans_key
,
*
members
)
pipeline
.
set
(
redis_bans_initialized_key
,
'1'
)
pipeline
.
execute
()
log_redis_error
(
"Initialized Redis ban state with "
+
str
(
len
(
members
))
+
" migrated entries"
)
except
redis
.
exceptions
.
RedisError
as
error
:
log_redis_error
(
"Unable to initialize Redis ban state: "
+
str
(
error
))
def
restore_bans_from_redis
():
"""Rebuild the local ban ipsets from the durable Redis set."""
try
:
members
=
r
.
smembers
(
redis_bans_key
)
except
redis
.
exceptions
.
RedisError
as
error
:
log_redis_error
(
"Unable to read Redis ban state: "
+
str
(
error
))
return
grouped
=
{
ipset_eterban_1
:
[],
ipset_eterban_1_ipv6
:
[]}
for
member
in
members
:
ip
=
member
.
decode
(
'utf-8'
)
if
isinstance
(
member
,
bytes
)
else
member
try
:
address
=
ipaddress
.
ip_address
(
ip
)
except
ValueError
:
log_redis_error
(
"Ignoring invalid Redis ban state entry: "
+
str
(
ip
))
continue
setname
=
ipset_eterban_1_ipv6
if
isinstance
(
address
,
ipaddress
.
IPv6Address
)
else
ipset_eterban_1
grouped
[
setname
]
.
append
(
str
(
address
))
for
setname
,
addresses
in
grouped
.
items
():
try
:
subprocess
.
run
([
'ipset'
,
'flush'
,
setname
],
check
=
True
,
timeout
=
10
)
for
ip
in
addresses
:
subprocess
.
run
([
'ipset'
,
'add'
,
setname
,
ip
,
'-exist'
],
check
=
True
,
timeout
=
10
)
except
(
OSError
,
subprocess
.
SubprocessError
)
as
error
:
log_redis_error
(
"Unable to restore "
+
setname
+
" from Redis: "
+
str
(
error
))
continue
log_redis_error
(
"Restored "
+
str
(
sum
(
len
(
addresses
)
for
addresses
in
grouped
.
values
()))
+
" ban entries from Redis"
)
def
persist_ban
(
ip
):
try
:
r
.
sadd
(
redis_bans_key
,
ip
)
return
True
except
redis
.
exceptions
.
RedisError
as
error
:
log_redis_error
(
"Unable to persist ban "
+
ip
+
" in Redis: "
+
str
(
error
))
return
False
def
remove_persisted_ban
(
ip
):
try
:
r
.
srem
(
redis_bans_key
,
ip
)
return
True
except
redis
.
exceptions
.
RedisError
as
error
:
log_redis_error
(
"Unable to remove ban "
+
ip
+
" from Redis: "
+
str
(
error
))
return
False
def
connect_redis
():
"""Connect to Redis and subscribe, retrying after a connection failure."""
while
True
:
...
...
@@ -349,9 +429,14 @@ if auto_mgr.enabled:
checker_thread
=
threading
.
Thread
(
target
=
auto_unban_checker
,
daemon
=
True
)
checker_thread
.
start
()
restore_ipset_eterban_1
()
# Older releases also kept the FireHOL set in this snapshot. The manual ban
# sets are flushed below and rebuilt from Redis, so the snapshot is only a
# migration source for them.
restore_legacy_ipsets
()
create_iptables_rules
()
create_ip6tables_rules
()
initialize_ban_state
()
restore_bans_from_redis
()
load_whitelist
()
...
...
@@ -366,6 +451,8 @@ def process_message(message):
log
.
write
(
info
)
log
.
flush
()
return
if
not
persist_ban
(
ip
):
return
if
isinstance
(
ipo
,
ipaddress
.
IPv6Address
):
ban
=
'ipset -A '
+
ipset_eterban_1_ipv6
+
' '
+
ip
else
:
...
...
@@ -391,7 +478,11 @@ def process_message(message):
elif
isinstance
(
ipo
,
ipaddress
.
IPv4Network
):
unban
=
'ipset -D '
+
ipset_eterban_1
+
' '
+
ip
#add = 'ipset -A ' + ipset_eterban_white + ' ' + ip
subprocess
.
call
(
unban
,
stdout
=
subprocess
.
PIPE
,
stderr
=
subprocess
.
PIPE
,
shell
=
True
)
if
subprocess
.
call
(
unban
+
' -exist'
,
stdout
=
subprocess
.
PIPE
,
stderr
=
subprocess
.
PIPE
,
shell
=
True
)
!=
0
:
log_redis_error
(
"Unable to remove ban from ipset: "
+
ip
)
return
if
not
remove_persisted_ban
(
ip
):
return
#subprocess.call (add, shell = True)
tcp_drop
=
'conntrack -D -s '
+
ip
subprocess
.
Popen
(
tcp_drop
,
stdout
=
subprocess
.
PIPE
,
stderr
=
subprocess
.
PIPE
,
shell
=
True
)
...
...
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