Compare commits
83 Commits
v0.8.0
...
72a0f941a1
| Author | SHA1 | Date | |
|---|---|---|---|
| 72a0f941a1 | |||
|
e9cd163ae7
|
|||
|
f778e25575
|
|||
|
21ab1802fe
|
|||
|
62517e6f4c
|
|||
|
8a7608dac6
|
|||
|
8d90a3770d
|
|||
|
4acb81b5a7
|
|||
|
49ab9c48c7
|
|||
|
28019ec171
|
|||
|
03b4009f96
|
|||
|
71502ff0c9
|
|||
|
ab59b356dc
|
|||
|
8595c6791d
|
|||
|
c64c94dceb
|
|||
|
a387e85569
|
|||
|
ab8466ada2
|
|||
|
d978343f4c
|
|||
|
14a6b9df0b
|
|||
|
298c7140a4
|
|||
|
527b6c8264
|
|||
|
0d707ac3c6
|
|||
|
b535195c1f
|
|||
|
3c12429f0e
|
|||
|
25ee39c0ec
|
|||
|
95e1f274f7
|
|||
|
5f72efd1bf
|
|||
|
e8826cb86b
|
|||
|
f12097b280
|
|||
|
3c040945bc
|
|||
|
0fdc07c0af
|
|||
|
a1345bd3e1
|
|||
|
d2f3640b75
|
|||
|
1363ff4bef
|
|||
| ec362f3c9a | |||
|
4c2d11423d
|
|||
|
671346e735
|
|||
|
b500d9fe57
|
|||
|
64c44085ea
|
|||
|
88264e1f4f
|
|||
|
954f3b4ce2
|
|||
|
9298f09b2e
|
|||
|
086aa784ea
|
|||
|
96ded2fc43
|
|||
|
6586e876d9
|
|||
|
90a8374c93
|
|||
|
0352bf7dd7
|
|||
|
d249bcdf16
|
|||
|
e0395f6dc1
|
|||
|
ab4496f6b8
|
|||
|
a084fef3d2
|
|||
|
9a9899958b
|
|||
|
9bfabd2148
|
|||
|
1298685ca4
|
|||
| 0ae0399aec | |||
|
a87eade21c
|
|||
|
447755dcc0
|
|||
|
6271682e4b
|
|||
|
c3571259a0
|
|||
|
9a406bedb6
|
|||
|
d25932ef7d
|
|||
|
a31386ed10
|
|||
|
9de460d2c9
|
|||
|
2a2ec666e6
|
|||
|
f198ec2c2c
|
|||
|
12bdd9ca3e
|
|||
|
d796b3a61b
|
|||
|
a49bf15023
|
|||
|
9597257a07
|
|||
|
bfcaca27a9
|
|||
|
14168d3765
|
|||
|
4587b522be
|
|||
|
d2e5db7f66
|
|||
|
eaa3513e03
|
|||
|
6d35c3e5bf
|
|||
|
3061d0f31e
|
|||
|
5e0fb5787a
|
|||
|
c0573c4e36
|
|||
|
8468fe851b
|
|||
|
6438358a53
|
|||
|
489c5c0cbe
|
|||
|
7c15813b0e
|
|||
|
3515b66dc7
|
@@ -1,4 +1,86 @@
|
||||
## 0.11.0 (7.5.2026)
|
||||
#### Русский
|
||||
* В настройки файла `firewall.toml` добавлен параметр `options.cache`.
|
||||
* Этот параметр включает кэширование, чтобы избежать постоянной компиляции команд nftables во временный файл. Файл кэша изменяется после изменения настроек или обновления версии программы. (`Включено по умолчанию`)
|
||||
* Логика добавления правил в nftables была переработана.
|
||||
* Команды теперь собираются во временном файле.
|
||||
* Запрос выполняется с использованием параметра -f.
|
||||
* Переработана логика обновления данных по блокировке IP адресов, которые получаем через другие сервисы.
|
||||
* Список IP адресов собирается в файл.
|
||||
* Запрос выполняется с использованием параметра -f.
|
||||
* Обновлена версия go-nftables-client до v0.2.1.
|
||||
* Улучшен вывод `Uptime` в команде `kor-elf-shield status`.
|
||||
* Улучшен вывод времени блокировки в уведомлениях.
|
||||
***
|
||||
#### English
|
||||
* The `options.cache` parameter has been added to the `firewall.toml` file settings.
|
||||
* This parameter enables caching to avoid constantly compiling nftables commands into a temporary file. The cache file changes after changing settings or updating the program version. (`Enabled by default`)
|
||||
* The logic for adding rules to nftables has been reworked.
|
||||
* Commands are now collected in a temporary file.
|
||||
* The query is executed using the -f parameter.
|
||||
* The logic for updating data on blocking IP addresses obtained through other services has been reworked.
|
||||
* The list of IP addresses is collected into a file.
|
||||
* The query is executed using the -f parameter.
|
||||
* Updated go-nftables-client to v0.2.1.
|
||||
* Improved `Uptime` output in `kor-elf-shield status` command.
|
||||
* Improved display of blocking time in notifications.
|
||||
***
|
||||
## 0.10.0 (12.4.2026)
|
||||
#### Русский
|
||||
* При автоматической блокировке добавил возможность получать данные об IP-адресах (континент, страна, город, часовой пояс).
|
||||
* В файл analyzer.toml добавлен параметр type к [[logAlert.rules.patterns.values]] в котором можно указать тип "ip". Это позволит для этого поля получить данные об IP-адресе при отправке оповещения.
|
||||
* Для получения данных об IP-адресах можно вызвать команду `kor-elf-shield geoip info <ip_address>`.
|
||||
* Можно принудительно обновить базу geoip командой `kor-elf-shield geoip refresh`.
|
||||
* Исправлена ошибка, когда в уведомлениях приходили лишние записи logs.
|
||||
* Улучшен вывод информации в комманде `kor-elf-shield status`.
|
||||
* В настройки файла kor-elf-shield.toml добавлен параметр otherSettingsPath.geoip.
|
||||
* Добавлен новый файл настроек geoip.toml. В этом файле настраиваются параметры для получения данных об IP-адресах.
|
||||
***
|
||||
#### English
|
||||
* Added the ability to receive data on IP addresses (continent, country, city, time zone) during automatic blocking.
|
||||
* The analyzer.toml file now has a new parameter, type, added to [[logAlert.rules.patterns.values]], allowing you to specify the "ip" type. This will allow this field to retrieve IP address data when sending an alert.
|
||||
* To obtain IP address data, you can use the `kor-elf-shield geoip info <ip_address>` command.
|
||||
* You can force a geoip database update with the `kor-elf-shield geoip refresh` command.
|
||||
* Fixed a bug where notifications contained extra logs.
|
||||
* Improved output of information in the `kor-elf-shield status` command.
|
||||
* The otherSettingsPath.geoip parameter has been added to the kor-elf-shield.toml file.
|
||||
* A new geoip.toml settings file has been added. This file configures parameters for retrieving IP address data.
|
||||
***
|
||||
## 0.9.0 (21.3.2026)
|
||||
#### Русский
|
||||
* Добавилась поддержка Port knocking.
|
||||
* В firewall.toml добавился раздел Port knocking.
|
||||
* Теперь вы можете получить список IP-адресов от различных сервисов для блокировки доступа:
|
||||
* Spamhaus Don't Route Or Peer Lists
|
||||
* DShield.org Recommended Block List
|
||||
* TOR Exit Nodes List
|
||||
* Project Honey Pot Directory of Dictionary Attacker IPs
|
||||
* C.I. Army Malicious IP List
|
||||
* BruteForceBlocker IP List
|
||||
* Blocklist.de
|
||||
* Stop Forum Spam
|
||||
* GreenSnow Hack List
|
||||
* В настройки файла kor-elf-shield.toml добавлен параметр otherSettingsPath.blocklists.
|
||||
* Добавлен новый файл настроек, blocklists.toml. Он содержит параметры для получения списка IP-адресов для блокировки.
|
||||
***
|
||||
#### English
|
||||
* Added support for port knocking.
|
||||
* A Port knocking section has been added to firewall.toml.
|
||||
* Now you can get a list of IP addresses from various services to block access:
|
||||
* Spamhaus Don't Route Or Peer Lists
|
||||
* DShield.org Recommended Block List
|
||||
* TOR Exit Nodes List
|
||||
* Project Honey Pot Directory of Dictionary Attacker IPs
|
||||
* C.I. Army Malicious IP List
|
||||
* BruteForceBlocker IP List
|
||||
* Blocklist.de
|
||||
* Stop Forum Spam
|
||||
* GreenSnow Hack List
|
||||
* Added the otherSettingsPath.blocklists parameter to the kor-elf-shield.toml settings.
|
||||
* Added a new settings file, blocklists.toml. It contains settings for obtaining a list of IP addresses to block.
|
||||
## 0.8.0 (9.3.2026)
|
||||
***
|
||||
#### Русский
|
||||
* Теперь можно тонко настроить блокировку портов для IP адреса, который пытается подобрать пароль.
|
||||
* В файл настроек analyzer.toml в [[bruteForceProtection.groups]] добавлен новый параметр "block_type".
|
||||
* В файл настроек analyzer.toml в [[bruteForceProtection.groups]] добавлен новый параметр "ports".
|
||||
|
||||
@@ -12,6 +12,34 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
git.kor-elf.net/kor-elf-shield/blocklist
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Leonid Nikitin (kor-elf)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
git.kor-elf.net/kor-elf-shield/geoip2
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Leonid Nikitin (kor-elf)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
github.com/fsnotify/fsnotify
|
||||
|
||||
Copyright © 2012 The Go Authors. All rights reserved.
|
||||
@@ -118,6 +146,46 @@ SOFTWARE.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
github.com/oschwald/geoip2-golang/v2
|
||||
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2015, Gregory J. Oschwald <oschwald@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
github.com/oschwald/maxminddb-golang/v2
|
||||
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2015, Gregory J. Oschwald <oschwald@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
github.com/pelletier/go-toml/v2
|
||||
|
||||
The bulk of github.com/pelletier/go-toml is distributed under the MIT license
|
||||
@@ -964,3 +1032,4 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -402,6 +402,7 @@ notify = true
|
||||
# [[logAlert.rules.patterns.values]]
|
||||
# name = "IP"
|
||||
# value = 3
|
||||
# type = "ip"
|
||||
#
|
||||
# ***
|
||||
# Specify the log settings to monitor for notifications.
|
||||
@@ -425,4 +426,5 @@ notify = true
|
||||
# [[logAlert.rules.patterns.values]]
|
||||
# name = "IP"
|
||||
# value = 3
|
||||
# type = "ip"
|
||||
###
|
||||
|
||||
@@ -0,0 +1,341 @@
|
||||
###############################################################################
|
||||
# РАЗДЕЛ:Настройки для определения списков блокировки IP-адресов
|
||||
# ***
|
||||
# SECTION:Settings for defining IP address block lists
|
||||
###############################################################################
|
||||
|
||||
###
|
||||
# Обеспечивает поддержку получения списков заблокированных IP-адресов.
|
||||
# !!! Для полученние данных нужно открыть исходящий порт 443. !!!
|
||||
# !!! После включения не забудьте включить необходимые списки, так как по умолчанию они все отключены. !!!
|
||||
# По умолчанию: false
|
||||
# ***
|
||||
# Enables support for retrieving IP block lists.
|
||||
# !!! To receive data, you need to open outgoing port 443. !!!
|
||||
# !!! After turning on, do not forget to turn on the necessary lists, as they are all turned off by default. !!!
|
||||
# Default: false
|
||||
###
|
||||
enabled = false
|
||||
|
||||
###
|
||||
# Spamhaus Don't Route Or Peer Lists (DROP IPv4)
|
||||
# Details: https://www.spamhaus.org/blocklists/do-not-route-or-peer/
|
||||
###
|
||||
[[sources]]
|
||||
enabled = false
|
||||
name = "SPAMDROP"
|
||||
url = "https://www.spamhaus.org/drop/drop_v4.json"
|
||||
format = "json"
|
||||
json_field = "cidr"
|
||||
limit = 0
|
||||
interval = 86400
|
||||
|
||||
###
|
||||
# Spamhaus Don't Route Or Peer Lists (DROP IPv6)
|
||||
# Details: https://www.spamhaus.org/blocklists/do-not-route-or-peer/
|
||||
###
|
||||
[[sources]]
|
||||
enabled = false
|
||||
name = "SPAMDROPV6"
|
||||
url = "https://www.spamhaus.org/drop/drop_v6.json"
|
||||
format = "json"
|
||||
json_field = "cidr"
|
||||
limit = 0
|
||||
interval = 86400
|
||||
|
||||
###
|
||||
# DShield.org Recommended Block List
|
||||
# Details: https://dshield.org
|
||||
###
|
||||
[[sources]]
|
||||
enabled = false
|
||||
name = "DSHIELD"
|
||||
url = "https://www.dshield.org/block.txt"
|
||||
format = "txt"
|
||||
txt_type = "cidr"
|
||||
txt_field_ip = 0
|
||||
txt_field_cidr = 2
|
||||
txt_separator = "\t"
|
||||
limit = 0
|
||||
interval = 86400
|
||||
###
|
||||
# Тот же список, только мы получаем данные не через CIDR, а через диапазон IP-адресов.
|
||||
# !!! Не рекомендуется включать DSHIELD и DSHIELD_INTERVAL одновременно. !!!
|
||||
# ***
|
||||
# The same list, only we receive data not via CIDR, but via a range of IP addresses.
|
||||
# !!! It is not recommended to turn on DSHIELD and DSHIELD_INTERVAL together. !!!
|
||||
###
|
||||
[[sources]]
|
||||
enabled = false
|
||||
name = "DSHIELD_INTERVAL"
|
||||
url = "https://www.dshield.org/block.txt"
|
||||
format = "txt"
|
||||
txt_type = "interval"
|
||||
txt_field_ip = 0
|
||||
txt_field_ip2 = 1
|
||||
txt_separator = "\t"
|
||||
limit = 0
|
||||
interval = 600
|
||||
|
||||
###
|
||||
# TOR Exit Nodes List
|
||||
# Details: https://trac.torproject.org/projects/tor/wiki/doc/TorDNSExitList
|
||||
###
|
||||
[[sources]]
|
||||
enabled = false
|
||||
name = "TOR"
|
||||
url = "https://check.torproject.org/torbulkexitlist"
|
||||
format = "txt"
|
||||
txt_type = "default"
|
||||
txt_field_ip = 0
|
||||
txt_separator = " "
|
||||
limit = 0
|
||||
interval = 86400
|
||||
|
||||
###
|
||||
# Project Honey Pot Directory of Dictionary Attacker IPs
|
||||
# Details: https://www.projecthoneypot.org/
|
||||
###
|
||||
[[sources]]
|
||||
enabled = false
|
||||
name = "HONEYPOT"
|
||||
url = "https://www.projecthoneypot.org/list_of_ips.php?t=d&rss=1"
|
||||
format = "rss"
|
||||
rss_tag = "item"
|
||||
rss_field = "title"
|
||||
rss_field_ip = 0
|
||||
rss_field_separator = "|"
|
||||
limit = 0
|
||||
interval = 86400
|
||||
|
||||
###
|
||||
# C.I. Army Malicious IP List
|
||||
# Details: https://www.ciarmy.com/
|
||||
###
|
||||
[[sources]]
|
||||
enabled = false
|
||||
name = "CIARMY"
|
||||
url = "https://www.ciarmy.com/list/ci-badguys.txt"
|
||||
format = "txt"
|
||||
txt_type = "default"
|
||||
txt_field_ip = 0
|
||||
txt_separator = " "
|
||||
limit = 0
|
||||
interval = 86400
|
||||
|
||||
###
|
||||
# BruteForceBlocker IP List
|
||||
# Details: https://danger.rulez.sk/index.php/bruteforceblocker/
|
||||
###
|
||||
[[sources]]
|
||||
enabled = false
|
||||
name = "BFB"
|
||||
url = "https://danger.rulez.sk/projects/bruteforceblocker/blist.php"
|
||||
format = "txt"
|
||||
txt_type = "default"
|
||||
txt_field_ip = 0
|
||||
txt_separator = "\t"
|
||||
limit = 0
|
||||
interval = 86400
|
||||
|
||||
###
|
||||
# Blocklist.de
|
||||
# Details: https://www.blocklist.de
|
||||
# В первом списке отображаются только IP-адреса, добавленные за последний час.
|
||||
# ***
|
||||
# This first list only retrieves the IP addresses added in the last hour.
|
||||
###
|
||||
[[sources]]
|
||||
enabled = false
|
||||
name = "BDE"
|
||||
url = "https://api.blocklist.de/getlast.php?time=3600"
|
||||
format = "txt"
|
||||
txt_type = "default"
|
||||
txt_field_ip = 0
|
||||
txt_separator = "\t"
|
||||
limit = 0
|
||||
interval = 86400
|
||||
###
|
||||
# Второй список содержит все IP-адреса, добавленные за последние 48 часов.
|
||||
# Обычно это очень большой список (более 10000 записей), поэтому убедитесь, что у вас
|
||||
# есть необходимые ресурсы для его использования.
|
||||
# !!! Не рекомендуется включать BDE и BDEALL одновременно. !!!
|
||||
# ***
|
||||
# This second list retrieves all the IP addresses added in the last 48 hours
|
||||
# and is usually a very large list (over 10000 entries), so be sure that you
|
||||
# have the resources available to use it
|
||||
# !!! It is not recommended to turn on BDE and BDEALL together. !!!
|
||||
###
|
||||
[[sources]]
|
||||
enabled = false
|
||||
name = "BDEALL"
|
||||
url = "https://lists.blocklist.de/lists/all.txt"
|
||||
format = "txt"
|
||||
txt_type = "default"
|
||||
txt_field_ip = 0
|
||||
txt_separator = "\t"
|
||||
limit = 0
|
||||
interval = 86400
|
||||
|
||||
# Stop Forum Spam (IPv4)
|
||||
# Details: https://www.stopforumspam.com/downloads
|
||||
# Многие из доступных списков содержат огромное количество IP-адресов,
|
||||
# поэтому следует проявлять осторожность при выборе из этих списков.
|
||||
# ***
|
||||
# Many of the lists available contain a vast number of IP addresses so special
|
||||
# care needs to be made when selecting from their lists.
|
||||
[[sources]]
|
||||
enabled = false
|
||||
name = "STOPFORUMSPAM"
|
||||
url = "https://www.stopforumspam.com/downloads/listed_ip_1.zip"
|
||||
zip = true
|
||||
format = "txt"
|
||||
txt_type = "default"
|
||||
txt_field_ip = 0
|
||||
txt_separator = " "
|
||||
limit = 0
|
||||
interval = 86400
|
||||
|
||||
# Stop Forum Spam (IPv6)
|
||||
# Details: https://www.stopforumspam.com/downloads
|
||||
# Многие из доступных списков содержат огромное количество IP-адресов,
|
||||
# поэтому следует проявлять осторожность при выборе из этих списков.
|
||||
# ***
|
||||
# Many of the lists available contain a vast number of IP addresses so special
|
||||
# care needs to be made when selecting from their lists.
|
||||
[[sources]]
|
||||
enabled = false
|
||||
name = "STOPFORUMSPAMV6"
|
||||
url = "https://www.stopforumspam.com/downloads/listed_ip_1_ipv6.zip"
|
||||
zip = true
|
||||
format = "txt"
|
||||
txt_type = "default"
|
||||
txt_field_ip = 0
|
||||
txt_separator = " "
|
||||
limit = 0
|
||||
interval = 86400
|
||||
|
||||
###
|
||||
# GreenSnow Hack List
|
||||
# Details: https://greensnow.co
|
||||
###
|
||||
[[sources]]
|
||||
enabled = false
|
||||
name = "GREENSNOW"
|
||||
url = "https://blocklist.greensnow.co/greensnow.txt"
|
||||
format = "txt"
|
||||
txt_type = "default"
|
||||
txt_field_ip = 0
|
||||
txt_separator = " "
|
||||
limit = 0
|
||||
interval = 86400
|
||||
|
||||
###############################################################################
|
||||
# Если вы хотите реализовать свой собственный список или хотите понять параметры,
|
||||
# описание параметров вам может помочь:
|
||||
# ***
|
||||
# If you want to implement your own list or want to understand the parameters,
|
||||
# the parameter description might help:
|
||||
###############################################################################
|
||||
#
|
||||
# [[sources]]
|
||||
#
|
||||
# Включает или выключает получения данных с этого списка
|
||||
# ***
|
||||
# Enables or disables retrieving data from this list
|
||||
# enabled = false
|
||||
#
|
||||
# Имя, которое будет использоваться в создание set в nftables.
|
||||
# Имя должно быть уникальное и разрешены символы "a-z, A-Z, -, _"
|
||||
# ***
|
||||
# The name that will be used when creating a set in nftables.
|
||||
# The name must be unique and the characters "a-z, A-Z, -, _" are allowed.
|
||||
# name = "SPAMDROP"
|
||||
#
|
||||
# Адрес по которому будет программа обращаться, чтобы получить список IP адресов для блокировки.
|
||||
# ***
|
||||
# The address that the program will contact to obtain a list of IP addresses to block.
|
||||
# url = "https://www.spamhaus.org/drop/drop_v4.json"
|
||||
#
|
||||
# Это максимальное количество IP-адресов из списка, которые можно использовать.
|
||||
# Значение 0 означает использование всех IP-адресов.
|
||||
# ***
|
||||
# This is the maximum number of IP addresses from the list that can be used.
|
||||
# A value of 0 means all IP addresses are used.
|
||||
# limit = 0
|
||||
#
|
||||
# Интервал обновления для загрузки списка должен составлять минимум 60 секунд (не рекомендуется),
|
||||
# но 86400 (в день) будет более чем достаточно.
|
||||
# ***
|
||||
# The refresh interval for loading the list should be at least 60 seconds (not recommended),
|
||||
# but 86400 (per day) will be more than enough.
|
||||
# interval = 86400
|
||||
#
|
||||
# Если получаемый файл в zip формате, то надо включить zip = true.
|
||||
# ***
|
||||
# If the file received is in zip format, then you must include zip = true.
|
||||
# zip = false
|
||||
#
|
||||
# Есть несколько форматов: "json", "txt", "rss".
|
||||
# ***
|
||||
# There are several formats: "json", "txt", "rss".
|
||||
# format = "json"
|
||||
#
|
||||
# Это поле используется, если `format = "json"`. Здесь мы указываем имя ключа, по которому получаем IP-адрес.
|
||||
# ***
|
||||
# This field is used if the `format = "json"`. Here we specify the name of the key by which we obtain the IP address.
|
||||
# json_field = "cidr"
|
||||
#
|
||||
# Это поле используется, если `format = "txt"`. Допустимые значения: "default", "cidr", "interval"
|
||||
# ***
|
||||
# This field is used if `format = "txt"`. Valid values: "default", "cidr", "interval"
|
||||
# txt_type = "cidr"
|
||||
#
|
||||
# Это поле используется, если `format = "txt"`. Это индекс поля для получения IP адреса.
|
||||
# Допустимые значения от 0 и выше.
|
||||
# ***
|
||||
# This field is used if `format = "txt"`. This is the field index for obtaining the IP address.
|
||||
# Valid values are 0 and higher.
|
||||
# txt_field_ip = 0
|
||||
#
|
||||
# Это поле используется, если `format = "txt"`. Это разделитель. Парсер использует его для разделения строки на поля.
|
||||
# ***
|
||||
# This field is used if `format = "txt"`. It's a delimiter. The parser uses it to divide the string into fields.
|
||||
# txt_separator = "\t"
|
||||
#
|
||||
# Это поле используется, если `format = "txt"` и `txt_type = "cidr"`. Указывает индекс поля для получения cidr.
|
||||
# Допустимые значения от 0 и выше.
|
||||
# ***
|
||||
# This field is used if `format = "txt"` and `txt_type = "cidr"`. Specifies the field index to retrieve the cidr.
|
||||
# Valid values are 0 and above.
|
||||
# txt_field_cidr = 2
|
||||
#
|
||||
# Это поле используется, если `format = "txt"` и `txt_type = "interval"`.
|
||||
# Указывает индекс поля для получения второго IP (по). Допустимые значения от 0 и выше.
|
||||
# ***
|
||||
# This field is used if `format = "txt"` and `txt_type = "interval"`.
|
||||
# Specifies the field index to retrieve the second IP (to). Valid values are 0 and higher.
|
||||
# txt_field_ip2 = 1
|
||||
#
|
||||
# Это поле используется, если `format = "rss"`. Указывает на родительский тег.
|
||||
# ***
|
||||
# This field is used if `format="rss"`. Points to the parent tag.
|
||||
# rss_tag = "item"
|
||||
#
|
||||
# Это поле используется, если `format = "rss"`. Указывает на тег, который находится внутри родительского тега.
|
||||
# ***
|
||||
# This field is used if `format="rss"`. Points to a tag that is inside the parent tag.
|
||||
# rss_field = "title"
|
||||
#
|
||||
# Это поле используется, если `format = "rss"`. Если нужно разделить получаемый текст.
|
||||
# ***
|
||||
# This field is used if `format = "rss"`. If you need to split the received text.
|
||||
# rss_field_separator = "|"
|
||||
#
|
||||
# Это поле используется, если `format = "rss"` и указан `rss_field_separator`. Это индекс поля для получения IP адреса.
|
||||
# Допустимые значения от 0 и выше.
|
||||
# ***
|
||||
# This field is used if `format = "rss"` and `rss_field_separator` is specified. This is the field index for obtaining the IP address.
|
||||
# Valid values are 0 and above.
|
||||
# rss_field_ip = 0
|
||||
###
|
||||
@@ -300,6 +300,17 @@ icmp_strict = false
|
||||
###############################################################################
|
||||
[options]
|
||||
|
||||
###
|
||||
# Включает кэширование, чтобы избежать постоянной компиляции команд nftables во временный файл.
|
||||
# Файл кэша изменяется после изменения настроек или обновления версии программы.
|
||||
# По умолчанию: true
|
||||
# ***
|
||||
# Enables a cache to avoid constantly compiling nftables commands into a temporary file.
|
||||
# The cache file changes after changing settings or updating the program version.
|
||||
# Default: true
|
||||
###
|
||||
cache = true
|
||||
|
||||
###
|
||||
# Переключения режима очистки фаервола nftables. Если указать "own", то может получиться конфликт в правилах.
|
||||
# Может спровоцировать проблему в безопасности. Указывайте "own" если вы уверены в своих действиях.
|
||||
@@ -508,6 +519,67 @@ forward_drop = "drop"
|
||||
###
|
||||
forward_priority = -10
|
||||
|
||||
###############################################################################
|
||||
# РАЗДЕЛ:Port knocking
|
||||
# ***
|
||||
# SECTION:Port knocking
|
||||
###############################################################################
|
||||
###
|
||||
# Тут можно настрить Port knocking. Это когда надо открыть порт, только поcле определённых стуков по определённому портам.
|
||||
#
|
||||
# Пример:
|
||||
# [[portKnocking]]
|
||||
# name = "ssh" # Имя должно быть уникальное и разрешены символы: "a-z, A-Z, -, _"
|
||||
# port = 22 # Номер порта, который нужно открыть после всех стуков
|
||||
# protocol = "tcp" # Протокол: tcp, udp
|
||||
# ip_version = "ip4" # Версия IP: ip4, ip6
|
||||
# [[portKnocking.knock]] # Первый стук
|
||||
# port = 2222 # Порт стука
|
||||
# protocol = "tcp" # Протокол: tcp, udp
|
||||
# timeout = 30 # Время в секундах на которое работает стук
|
||||
# action = "drop" # Во время стука, какой ответ отдавать: accept, return, drop or reject
|
||||
## Лучше установить для параметра "action" значение "drop", чтобы любой, кто попытается угадать такие порты для "стука",
|
||||
## не смог отличить их от заблокированного порта.
|
||||
# [[portKnocking.knock]] # Второй стук (можно добавлять сколько хотите)
|
||||
# port = 2225
|
||||
# protocol = "tcp"
|
||||
# timeout = 30
|
||||
# action = "drop"
|
||||
#
|
||||
# Рассмотрим пример. Мы назвали настройку для portKnocking "ssh". В nftables будут созданны наборы sets таким образом:
|
||||
# knock_ssh_0, knock_ssh_1. Мы открываем 22/tcp порт для IP адреса, который прошёл в данном случае два стука.
|
||||
# 1 стук надо сделать на 2222/tcp и в течении 30 секунд надо сделать второй стук на порт 2225/tcp.
|
||||
# После второго стука будет открыт для данного IP адреса порт 22/tcp на 30 секунд.
|
||||
#
|
||||
# ***
|
||||
#
|
||||
# Here you can configure port knocking. This allows you to open a port only after certain knocks on a specific ports.
|
||||
#
|
||||
# Example:
|
||||
# [[portKnocking]]
|
||||
# name = "ssh" # The name must be unique and symbols are allowed: "a-z, A-Z, -, _"
|
||||
# port = 22 # The port number that needs to be opened after all the knocking
|
||||
# protocol = "tcp" # Protocol: tcp, udp
|
||||
# ip_version = "ip4" # IP version: ip4, ip6
|
||||
# [[portKnocking.knock]] # The first knock
|
||||
# port = 2222 # Port of knocking
|
||||
# protocol = "tcp" # Protocol: tcp, udp
|
||||
# timeout = 30 # The time in seconds for which the knocking works
|
||||
# action = "drop" # When knocking, what answer should I give: accept, return, drop or reject
|
||||
## It's best to set the "action" parameter to "drop" so that anyone trying to guess such ports for "knocking"
|
||||
## won't be able to distinguish them from a blocked port.
|
||||
# [[portKnocking.knock]] # Second knock (you can add as many as you want)
|
||||
# port = 2225
|
||||
# protocol = "tcp"
|
||||
# timeout = 30
|
||||
# action = "drop"
|
||||
#
|
||||
# Let's look at an example. We named the portKnocking setting "ssh." Sets will be created in nftables as follows:
|
||||
# knock_ssh_0, knock_ssh_1. We open port 22/tcp for the IP address that, in this case, has been knocked twice.
|
||||
# The first knock should be made on 2222/tcp, and within 30 seconds, a second knock should be made on port 2225/tcp.
|
||||
# After the second knock, port 22/tcp will be opened for this IP address for 30 seconds.
|
||||
###
|
||||
|
||||
###############################################################################
|
||||
# РАЗДЕЛ:Именование метаданных
|
||||
# ***
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
###############################################################################
|
||||
# РАЗДЕЛ:Настройки для получения данных об IP-адресах
|
||||
# ***
|
||||
# SECTION:Settings for obtaining IP address data
|
||||
###############################################################################
|
||||
|
||||
###
|
||||
# Включает или выключает получения данных об IP-адресах.
|
||||
# !!! Не забудьте перед включением настроить данные от сервиса maxmind.com !!!
|
||||
# false = Выключает.
|
||||
# true = Включает.
|
||||
#
|
||||
# По умолчанию: false
|
||||
# ***
|
||||
# Enables or disables retrieval of IP address data.
|
||||
# !!! Don't forget to configure the data from the maxmind.com service before turning it on !!!
|
||||
# false = Disables.
|
||||
# true = Enables.
|
||||
#
|
||||
# Default: false
|
||||
###
|
||||
enabled = false
|
||||
|
||||
###
|
||||
# Мы указываем, через какой сервис мы будем получать данные об IP-адресе.
|
||||
# По умолчанию: maxmind
|
||||
# ***
|
||||
# We indicate through which service we will receive data about the IP address.
|
||||
# Default: maxmind
|
||||
###
|
||||
service = "maxmind"
|
||||
|
||||
###############################################################################
|
||||
# СЕРВИС: MaxMind https://www.maxmind.com/en/geolite-free-ip-geolocation-data
|
||||
# ***
|
||||
# SERVICE: MaxMind https://www.maxmind.com/en/geolite-free-ip-geolocation-data
|
||||
###############################################################################
|
||||
[maxmind]
|
||||
|
||||
###
|
||||
# ID пользователя.
|
||||
# Чтобы получить, нужно вначале зарегестрироватья тут: https://www.maxmind.com/en/geolite2/signup
|
||||
# ***
|
||||
# User ID.
|
||||
# To receive it, you must first register here: https://www.maxmind.com/en/geolite2/signup
|
||||
###
|
||||
username = ""
|
||||
|
||||
###
|
||||
# Лицензионный ключ. Его надо сгенерировать в личном кабинете.
|
||||
# ***
|
||||
# License Key. It must be generated in your personal account.
|
||||
####
|
||||
password = ""
|
||||
|
||||
###
|
||||
# Интервал обновления для обновления базы в секундах. Рекомендуется оставить на 86400 (1 раз в день).
|
||||
# ***
|
||||
# Update interval for database updates in seconds. Recommended setting: 86400 (once per day).
|
||||
###
|
||||
interval = 86400
|
||||
|
||||
###
|
||||
# Язык на котором будут возвращаться данные об IP-адрессе.
|
||||
#
|
||||
# Допустимые значения:
|
||||
# Russian
|
||||
# English
|
||||
# Spanish
|
||||
# French
|
||||
# German
|
||||
# Japanese
|
||||
# Brazilian Portuguese
|
||||
# Simplified Chinese
|
||||
#
|
||||
# ***
|
||||
# The language in which IP address data will be returned.
|
||||
#
|
||||
# Acceptable values:
|
||||
# Russian
|
||||
# English
|
||||
# Spanish
|
||||
# French
|
||||
# German
|
||||
# Japanese
|
||||
# Brazilian Portuguese
|
||||
# Simplified Chinese
|
||||
#
|
||||
###
|
||||
language = "Russian"
|
||||
|
||||
###
|
||||
# Если по каким-то причинам захотите поменять адрес.
|
||||
# Например: у сервиса поменялся адрес или у Вас есть платная подписка.
|
||||
# Не забудьте поле измененяи параметра url убрать комментарий.
|
||||
# ***
|
||||
# If for some reason you want to change your address.
|
||||
# For example: the service's address has changed or you have a paid subscription.
|
||||
# Don't forget to remove the comment from the change field for the url parameter.
|
||||
###
|
||||
#url = ""
|
||||
@@ -273,3 +273,25 @@ analyzer = "/etc/kor-elf-shield/analyzer.toml"
|
||||
# Default: /etc/kor-elf-shield/docker.toml
|
||||
###
|
||||
docker = "/etc/kor-elf-shield/docker.toml"
|
||||
|
||||
###
|
||||
# Укажите путь к настройкам для получения списков блокировки IP-адресов.
|
||||
# Файл должен иметь расширение .toml.
|
||||
# По умолчанию: /etc/kor-elf-shield/blocklists.toml
|
||||
# ***
|
||||
# Specify the path to the settings for retrieving IP address block lists.
|
||||
# The file must have the .toml extension.
|
||||
# Default: /etc/kor-elf-shield/blocklists.toml
|
||||
###
|
||||
blocklists = "/etc/kor-elf-shield/blocklists.toml"
|
||||
|
||||
###
|
||||
# Укажите путь к настройкам для получения данных об IP-адресах.
|
||||
# Файл должен иметь расширение .toml.
|
||||
# По умолчанию: /etc/kor-elf-shield/geoip.toml
|
||||
# ***
|
||||
# Specify the path to the settings for obtaining data on IP addresses.
|
||||
# The file must have the .toml extension.
|
||||
# Default: /etc/kor-elf-shield/geoip.toml
|
||||
###
|
||||
geoip = "/etc/kor-elf-shield/geoip.toml"
|
||||
|
||||
@@ -3,7 +3,9 @@ module git.kor-elf.net/kor-elf-shield/kor-elf-shield
|
||||
go 1.25
|
||||
|
||||
require (
|
||||
git.kor-elf.net/kor-elf-shield/go-nftables-client v0.1.1
|
||||
git.kor-elf.net/kor-elf-shield/blocklist v1.1.0
|
||||
git.kor-elf.net/kor-elf-shield/geoip2 v0.1.2
|
||||
git.kor-elf.net/kor-elf-shield/go-nftables-client v0.2.1
|
||||
github.com/nicksnyder/go-i18n/v2 v2.6.1
|
||||
github.com/nxadm/tail v1.4.11
|
||||
github.com/spf13/viper v1.21.0
|
||||
@@ -18,6 +20,8 @@ require (
|
||||
require (
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
|
||||
github.com/oschwald/geoip2-golang/v2 v2.1.0 // indirect
|
||||
github.com/oschwald/maxminddb-golang/v2 v2.1.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/sagikazarmark/locafero v0.12.0 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
git.kor-elf.net/kor-elf-shield/go-nftables-client v0.1.1 h1:3oGtZ/r1YAdlvI16OkZSCaxcWztHe/33ITWfI2LaQm0=
|
||||
git.kor-elf.net/kor-elf-shield/go-nftables-client v0.1.1/go.mod h1:a7F+XdL1pK5P3ucQRR2EK/fABAP37LLBENiA4hX7L6A=
|
||||
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
git.kor-elf.net/kor-elf-shield/blocklist v1.1.0 h1:NS8be3TFBsUn+ft3oG5sAD56iJTGOkFH6GgjepEnS0s=
|
||||
git.kor-elf.net/kor-elf-shield/blocklist v1.1.0/go.mod h1:nNbQux5vbuoCa3wMiC2QsLb4tO1JLCssGzdljizcJUs=
|
||||
git.kor-elf.net/kor-elf-shield/geoip2 v0.1.2 h1:/J9U+h9H92hW6TtwCznkRANqhX5kvBpN4uV7xDbwXpM=
|
||||
git.kor-elf.net/kor-elf-shield/geoip2 v0.1.2/go.mod h1:ULMUjpd2I9ikkDDE69IlpKT4vR2/nlYT0cqoR2T95sM=
|
||||
git.kor-elf.net/kor-elf-shield/go-nftables-client v0.2.1 h1:B5u1uCYyrDlDlCSA03o/Djt/T0A3SgCeFsfZkq25Hwg=
|
||||
git.kor-elf.net/kor-elf-shield/go-nftables-client v0.2.1/go.mod h1:a7F+XdL1pK5P3ucQRR2EK/fABAP37LLBENiA4hX7L6A=
|
||||
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
|
||||
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
@@ -10,8 +13,6 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
@@ -20,12 +21,14 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.6.0 h1:C/m2NNWNiTB6SK4Ao8df5EWm3JETSTIGNXBpMJTxzxQ=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.6.0/go.mod h1:88sRqr0C6OPyJn0/KRNaEz1uWorjxIKP7rUUcvycecE=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.6.1 h1:JDEJraFsQE17Dut9HFDHzCoAWGEQJom5s0TRd17NIEQ=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.6.1/go.mod h1:Vee0/9RD3Quc/NmwEjzzD7VTZ+Ir7QbXocrkhOzmUKA=
|
||||
github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
|
||||
github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=
|
||||
github.com/oschwald/geoip2-golang/v2 v2.1.0 h1:DjnLhNJu9WHwTrmoiQFvgmyJoczhdnm7LB23UBI2Amo=
|
||||
github.com/oschwald/geoip2-golang/v2 v2.1.0/go.mod h1:qdVmcPgrTJ4q2eP9tHq/yldMTdp2VMr33uVdFbHBiBc=
|
||||
github.com/oschwald/maxminddb-golang/v2 v2.1.1 h1:lA8FH0oOrM4u7mLvowq8IT6a3Q/qEnqRzLQn9eH5ojc=
|
||||
github.com/oschwald/maxminddb-golang/v2 v2.1.1/go.mod h1:PLdx6PR+siSIoXqqy7C7r3SB3KZnhxWr1Dp6g0Hacl8=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
@@ -46,8 +49,6 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/urfave/cli/v3 v3.4.1 h1:1M9UOCy5bLmGnuu1yn3t3CB4rG79Rtoxuv1sPhnm6qM=
|
||||
github.com/urfave/cli/v3 v3.4.1/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=
|
||||
github.com/urfave/cli/v3 v3.6.2 h1:lQuqiPrZ1cIz8hz+HcrG0TNZFxU70dPZ3Yl+pSrH9A8=
|
||||
github.com/urfave/cli/v3 v3.6.2/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
|
||||
github.com/wneessen/go-mail v0.7.2 h1:xxPnhZ6IZLSgxShebmZ6DPKh1b6OJcoHfzy7UjOkzS8=
|
||||
@@ -56,26 +57,17 @@ go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
|
||||
go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
||||
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
|
||||
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/i18n"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func CmdGeoIP() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "geoip",
|
||||
Usage: i18n.Lang.T("cmd.daemon.geoip.Usage"),
|
||||
Commands: []*cli.Command{
|
||||
{
|
||||
Name: "info",
|
||||
Usage: i18n.Lang.T("cmd.daemon.geoip.info.Usage"),
|
||||
Description: i18n.Lang.T("cmd.daemon.geoip.info.Description"),
|
||||
Action: CmdGeoIPInfo,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "ip",
|
||||
Usage: i18n.Lang.T("cmd.daemon.geoip.info.FlagUsage.ip"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "refresh",
|
||||
Usage: i18n.Lang.T("cmd.daemon.geoip.refresh.Usage"),
|
||||
Description: i18n.Lang.T("cmd.daemon.geoip.refresh.Description"),
|
||||
Action: CmdGeoIPRefresh,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func CmdGeoIPInfo(_ context.Context, cmd *cli.Command) error {
|
||||
ip := net.ParseIP(cmd.Args().Get(0))
|
||||
if ip == nil {
|
||||
return errors.New("invalid ip address")
|
||||
}
|
||||
|
||||
sock, err := newSocket()
|
||||
if err != nil {
|
||||
return errors.New(i18n.Lang.T("daemon is not running"))
|
||||
}
|
||||
defer func() {
|
||||
_ = sock.Close()
|
||||
}()
|
||||
|
||||
result, err := sock.SendCommand("geoip_info", map[string]string{
|
||||
"ip": ip.String(),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CmdGeoIPRefresh(_ context.Context, _ *cli.Command) error {
|
||||
sock, err := newSocket()
|
||||
if err != nil {
|
||||
return errors.New(i18n.Lang.T("daemon is not running"))
|
||||
}
|
||||
defer func() {
|
||||
_ = sock.Close()
|
||||
}()
|
||||
|
||||
result, err := sock.Send("geoip_refresh")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if result != "ok" {
|
||||
return errors.New(i18n.Lang.T("cmd.error", map[string]any{
|
||||
"Error": result,
|
||||
}))
|
||||
}
|
||||
|
||||
fmt.Println(i18n.Lang.T("geoip_refresh_success"))
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -3,16 +3,19 @@ package daemon
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/blocklist"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db/repository"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/geoip"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/info"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/notifications"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/i18n"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/setting"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
@@ -75,7 +78,16 @@ func runDaemon(ctx context.Context, _ *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
d, err := daemon.NewDaemon(config, logger, notificationsService, dockerService)
|
||||
blocklistService := newBlocklistService(ctx, repositories.Blocklist(), logger)
|
||||
|
||||
geoIPService := newGeoIPService(config.DataDir, logger)
|
||||
defer func() {
|
||||
_ = geoIPService.Close()
|
||||
}()
|
||||
|
||||
daemonInfo := newDaemonInfo(repositories.Metadata(), setting.Config.ListPathConfigFiles(), logger)
|
||||
|
||||
d, err := daemon.NewDaemon(daemonInfo, config, logger, notificationsService, dockerService, blocklistService, geoIPService)
|
||||
if err != nil {
|
||||
logger.Fatal(err.Error())
|
||||
|
||||
@@ -96,6 +108,23 @@ func runDaemon(ctx context.Context, _ *cli.Command) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func newDaemonInfo(repo repository.MetadataRepository, listPathFiles map[string]string, logger log.Logger) info.Info {
|
||||
metaFirewallFileNft := info.NewMetadataFirewallFileNft(repo)
|
||||
metadataContainer := info.NewMetadataContainer(metaFirewallFileNft)
|
||||
|
||||
return info.New(
|
||||
setting.AppVer,
|
||||
info.IsVersionChanged(repo, setting.AppVer, logger),
|
||||
|
||||
setting.AppBuiltWith,
|
||||
setting.AppStartTime,
|
||||
|
||||
info.IsSettingsChanged(repo, listPathFiles, logger),
|
||||
|
||||
metadataContainer,
|
||||
)
|
||||
}
|
||||
|
||||
func newNotificationsService(queueRepository repository.NotificationsQueueRepository, logger log.Logger) (notifications.Notifications, error) {
|
||||
config, err := setting.Config.OtherSettingsPath.ToNotificationsConfig()
|
||||
if err != nil {
|
||||
@@ -123,3 +152,41 @@ func newDockerService(ctx context.Context, logger log.Logger) (dockerService doc
|
||||
|
||||
return dockerService, dockerSupport, nil
|
||||
}
|
||||
|
||||
func newBlocklistService(ctx context.Context, blocklistRepository repository.BlocklistRepository, logger log.Logger) blocklist.Blocklist {
|
||||
config, isEnabled, err := setting.Config.OtherSettingsPath.ToBlocklistConfig(logger)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("Failed to create blocklist service: %s", err))
|
||||
return blocklist.NewFalseBlocklist()
|
||||
}
|
||||
if !isEnabled {
|
||||
return blocklist.NewFalseBlocklist()
|
||||
}
|
||||
|
||||
blocklistConfig := blocklist.Config{
|
||||
BlocklistRepository: blocklistRepository,
|
||||
Sources: config,
|
||||
PathDir: strings.TrimRight(setting.Config.DataDir, "/") + "/blocklists",
|
||||
}
|
||||
|
||||
blocklistService, err := blocklist.New(blocklistConfig, ctx, logger)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
return blocklist.NewFalseBlocklist()
|
||||
}
|
||||
|
||||
return blocklistService
|
||||
}
|
||||
|
||||
func newGeoIPService(dataDir string, logger log.Logger) geoip.GeoIP {
|
||||
config, geoIPSupport, err := setting.Config.OtherSettingsPath.ToConfig(dataDir, logger)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("Failed to create geoIP service: %s", err))
|
||||
return geoip.NewFalseGeoIP()
|
||||
}
|
||||
if !geoIPSupport || config.GeoIP == nil {
|
||||
return geoip.NewFalseGeoIP()
|
||||
}
|
||||
|
||||
return geoip.New(config, logger)
|
||||
}
|
||||
|
||||
@@ -32,11 +32,7 @@ func cmdStatus(_ context.Context, _ *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if result != "ok" {
|
||||
return errors.New(i18n.Lang.T("daemon is not running"))
|
||||
}
|
||||
|
||||
fmt.Println("ok")
|
||||
fmt.Println(result)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ func NewMainApp(appVer AppVersion, defaultConfigPath string) *cli.Command {
|
||||
daemon.CmdReopenLogger(),
|
||||
daemon.CmdNotifications(),
|
||||
daemon.CmdBlock(),
|
||||
daemon.CmdGeoIP(),
|
||||
}
|
||||
|
||||
return app
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
analysisServices "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/analyzer/log/analysis"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/analyzer/log/analysis/brute_force_protection_group"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/geoip"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/notifications"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
)
|
||||
@@ -30,7 +31,14 @@ type analyzer struct {
|
||||
logChan chan analysisServices.Entry
|
||||
}
|
||||
|
||||
func New(config config2.Config, blockService brute_force_protection_group.BlockService, repositories db.Repositories, logger log.Logger, notify notifications.Notifications) Analyzer {
|
||||
func New(
|
||||
config config2.Config,
|
||||
blockService brute_force_protection_group.BlockService,
|
||||
repositories db.Repositories,
|
||||
logger log.Logger,
|
||||
notify notifications.Notifications,
|
||||
ipInfo geoip.Info,
|
||||
) Analyzer {
|
||||
var journalMatches []string
|
||||
journalMatchesUniq := map[string]struct{}{}
|
||||
|
||||
@@ -66,7 +74,7 @@ func New(config config2.Config, blockService brute_force_protection_group.BlockS
|
||||
|
||||
systemdService := analyzerLog.NewSystemd(config.BinPath.Journalctl, journalMatches, logger)
|
||||
filesService := analyzerLog.NewFileMonitoring(files, logger)
|
||||
analysisService := analyzerLog.NewAnalysis(rulesIndex, blockService, repositories, logger, notify)
|
||||
analysisService := analyzerLog.NewAnalysis(rulesIndex, blockService, repositories, logger, notify, ipInfo)
|
||||
|
||||
return &analyzer{
|
||||
config: config,
|
||||
|
||||
@@ -119,4 +119,11 @@ type AlertRegexPattern struct {
|
||||
type PatternValue struct {
|
||||
Name string
|
||||
Value uint8
|
||||
Type PatternTypeValue
|
||||
}
|
||||
|
||||
type PatternTypeValue string
|
||||
|
||||
const (
|
||||
PatternValueIP PatternTypeValue = "ip"
|
||||
)
|
||||
|
||||
@@ -33,6 +33,7 @@ func NewLoginSSH(isNotify bool) ([]*Source, error) {
|
||||
{
|
||||
Name: "IP",
|
||||
Value: 3,
|
||||
Type: PatternValueIP,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/analyzer/log/analysis/alert_group"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/analyzer/log/analysis/brute_force_protection_group"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/geoip"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/notifications"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
)
|
||||
@@ -22,13 +23,27 @@ type analysis struct {
|
||||
bruteForceProtectionService analysisServices.BruteForceProtection
|
||||
}
|
||||
|
||||
func NewAnalysis(rulesIndex *analysisServices.RulesIndex, blockService brute_force_protection_group.BlockService, repositories db.Repositories, logger log.Logger, notify notifications.Notifications) Analysis {
|
||||
func NewAnalysis(
|
||||
rulesIndex *analysisServices.RulesIndex,
|
||||
blockService brute_force_protection_group.BlockService,
|
||||
repositories db.Repositories,
|
||||
logger log.Logger,
|
||||
notify notifications.Notifications,
|
||||
ipInfo geoip.Info,
|
||||
) Analysis {
|
||||
alertGroupService := alert_group.NewGroup(repositories.AlertGroup(), logger)
|
||||
bruteForceProtectionGroupService := brute_force_protection_group.NewGroup(repositories.BruteForceProtectionGroup(), logger)
|
||||
|
||||
return &analysis{
|
||||
alertService: analysisServices.NewAlert(rulesIndex, alertGroupService, logger, notify),
|
||||
bruteForceProtectionService: analysisServices.NewBruteForceProtection(rulesIndex, bruteForceProtectionGroupService, blockService, logger, notify),
|
||||
alertService: analysisServices.NewAlert(rulesIndex, alertGroupService, logger, notify, ipInfo),
|
||||
bruteForceProtectionService: analysisServices.NewBruteForceProtection(
|
||||
rulesIndex,
|
||||
bruteForceProtectionGroupService,
|
||||
blockService,
|
||||
logger,
|
||||
notify,
|
||||
ipInfo,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/analyzer/config"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/analyzer/log/analysis/alert_group"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/geoip"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/notifications"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/i18n"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
@@ -21,6 +22,7 @@ type alert struct {
|
||||
alertGroupService alert_group.Group
|
||||
logger log.Logger
|
||||
notify notifications.Notifications
|
||||
ipInfo geoip.Info
|
||||
}
|
||||
|
||||
type alertAnalyzeRuleReturn struct {
|
||||
@@ -35,12 +37,19 @@ type alertNotify struct {
|
||||
fields []*regexField
|
||||
}
|
||||
|
||||
func NewAlert(rulesIndex *RulesIndex, alertGroupService alert_group.Group, logger log.Logger, notify notifications.Notifications) Alert {
|
||||
func NewAlert(
|
||||
rulesIndex *RulesIndex,
|
||||
alertGroupService alert_group.Group,
|
||||
logger log.Logger,
|
||||
notify notifications.Notifications,
|
||||
ipInfo geoip.Info,
|
||||
) Alert {
|
||||
return &alert{
|
||||
rulesIndex: rulesIndex,
|
||||
alertGroupService: alertGroupService,
|
||||
logger: logger,
|
||||
notify: notify,
|
||||
ipInfo: ipInfo,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +118,7 @@ func (a *alert) analyzeRule(rule *config.AlertRule, message string) alertAnalyze
|
||||
result.fields = append(result.fields, ®exField{name: value.Name, value: i18n.Lang.T("unknown")})
|
||||
continue
|
||||
}
|
||||
result.fields = append(result.fields, ®exField{name: value.Name, value: message[start:end]})
|
||||
result.fields = append(result.fields, ®exField{name: value.Name, value: message[start:end], typeValue: value.Type})
|
||||
}
|
||||
|
||||
if len(pattern.Values) != len(result.fields) {
|
||||
@@ -145,11 +154,21 @@ func (a *alert) sendNotify(notify *alertNotify) {
|
||||
"Time": notify.time,
|
||||
}) + "\n"
|
||||
for _, field := range notify.fields {
|
||||
text += fmt.Sprintf("%s: %s\n", field.name, field.value)
|
||||
v := field.value
|
||||
if field.typeValue == config.PatternValueIP {
|
||||
if ipInfo, err := a.ipInfo(field.value); err != nil {
|
||||
a.logger.Error(fmt.Sprintf("Failed to get geoip info for ip %s: %s", v, err))
|
||||
} else {
|
||||
v = ipInfo
|
||||
}
|
||||
}
|
||||
text += fmt.Sprintf("%s: %s\n", field.name, v)
|
||||
}
|
||||
text += "\n" + i18n.Lang.T("log") + "\n"
|
||||
text += "\n" + i18n.Lang.T("log", map[string]any{
|
||||
"Count": len(notify.messages),
|
||||
}) + "\n"
|
||||
for _, message := range notify.messages {
|
||||
text += message + "\n"
|
||||
text += message + "\n\n"
|
||||
}
|
||||
a.notify.SendAsync(notifications.Message{Subject: subject, Body: text})
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ func (g *group) Analyze(alertGroup *config.AlertGroup, eventTime time.Time, mess
|
||||
}
|
||||
|
||||
g.logger.Debug(fmt.Sprintf("Alert not rate limited"))
|
||||
entityAlertGroup.LastLogs = []string{}
|
||||
analysisResult, entityAlertGroup = g.analysisResult(rateLimit, eventTime, message, entityAlertGroup)
|
||||
|
||||
return entityAlertGroup, nil
|
||||
|
||||
@@ -20,8 +20,9 @@ type Entry struct {
|
||||
}
|
||||
|
||||
type regexField struct {
|
||||
name string
|
||||
value string
|
||||
name string
|
||||
value string
|
||||
typeValue config.PatternTypeValue
|
||||
}
|
||||
|
||||
func getValueStartEndByRegexIndex(valueId int, idx []int) (start int, end int, err error) {
|
||||
|
||||
@@ -10,9 +10,11 @@ import (
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/analyzer/log/analysis/brute_force_protection_group"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/blocking"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/types"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/geoip"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/notifications"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/i18n"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/pkg/format"
|
||||
)
|
||||
|
||||
type BruteForceProtection interface {
|
||||
@@ -26,6 +28,7 @@ type bruteForceProtection struct {
|
||||
blockService brute_force_protection_group.BlockService
|
||||
logger log.Logger
|
||||
notify notifications.Notifications
|
||||
ipInfo geoip.Info
|
||||
}
|
||||
|
||||
type bruteForceProtectionAnalyzeRuleReturn struct {
|
||||
@@ -45,13 +48,21 @@ type bruteForceProtectionNotify struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func NewBruteForceProtection(rulesIndex *RulesIndex, groupService brute_force_protection_group.Group, blockService brute_force_protection_group.BlockService, logger log.Logger, notify notifications.Notifications) BruteForceProtection {
|
||||
func NewBruteForceProtection(
|
||||
rulesIndex *RulesIndex,
|
||||
groupService brute_force_protection_group.Group,
|
||||
blockService brute_force_protection_group.BlockService,
|
||||
logger log.Logger,
|
||||
notify notifications.Notifications,
|
||||
ipInfo geoip.Info,
|
||||
) BruteForceProtection {
|
||||
return &bruteForceProtection{
|
||||
rulesIndex: rulesIndex,
|
||||
groupService: groupService,
|
||||
blockService: blockService,
|
||||
logger: logger,
|
||||
notify: notify,
|
||||
ipInfo: ipInfo,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,7 +284,14 @@ func (p *bruteForceProtection) sendNotify(subject string, notify *bruteForceProt
|
||||
"Error": notify.err.Error(),
|
||||
}) + "\n"
|
||||
}
|
||||
text += "IP: " + notify.ip.String() + "\n"
|
||||
|
||||
ipInfo, err := p.ipInfo(notify.ip.String())
|
||||
if err != nil {
|
||||
ipInfo = notify.ip.String()
|
||||
p.logger.Error(fmt.Sprintf("Failed to get geoip info for ip %s: %s", notify.ip, err))
|
||||
}
|
||||
|
||||
text += "IP: " + ipInfo + "\n"
|
||||
if len(notify.ports) > 0 {
|
||||
var ports []string
|
||||
for _, port := range notify.ports {
|
||||
@@ -284,7 +302,7 @@ func (p *bruteForceProtection) sendNotify(subject string, notify *bruteForceProt
|
||||
}) + "\n"
|
||||
}
|
||||
text += i18n.Lang.T("blockSec", map[string]any{
|
||||
"BlockSec": notify.blockSec,
|
||||
"BlockSec": format.HumanDuration(time.Duration(notify.blockSec) * time.Second),
|
||||
}) + "\n"
|
||||
text += i18n.Lang.T("time", map[string]any{
|
||||
"Time": notify.time,
|
||||
@@ -292,9 +310,11 @@ func (p *bruteForceProtection) sendNotify(subject string, notify *bruteForceProt
|
||||
for _, field := range notify.fields {
|
||||
text += fmt.Sprintf("%s: %s\n", field.name, field.value)
|
||||
}
|
||||
text += "\n" + i18n.Lang.T("log") + "\n"
|
||||
text += "\n" + i18n.Lang.T("log", map[string]any{
|
||||
"Count": len(notify.messages),
|
||||
}) + "\n"
|
||||
for _, message := range notify.messages {
|
||||
text += message + "\n"
|
||||
text += message + "\n\n"
|
||||
}
|
||||
p.notify.SendAsync(notifications.Message{Subject: subject, Body: text})
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ func (g *group) Analyze(group *brute_force_protection.Group, eventTime time.Time
|
||||
}
|
||||
|
||||
g.logger.Debug(fmt.Sprintf("Brute force protection not rate limited"))
|
||||
entityGroup.LastLogs = []string{}
|
||||
analysisResult, entityGroup = g.analysisResult(rateLimit, eventTime, message, entityGroup)
|
||||
|
||||
return entityGroup, nil
|
||||
|
||||
@@ -0,0 +1,280 @@
|
||||
package blocklist
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db/entity"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db/repository"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/block"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/pkg/filesystem"
|
||||
)
|
||||
|
||||
type Blocklist interface {
|
||||
Names() []string
|
||||
NftReload(blocks map[string]block.Blocklist) error
|
||||
Run()
|
||||
Close() error
|
||||
}
|
||||
|
||||
type updateSource struct {
|
||||
forcedly bool
|
||||
source *SourceConfig
|
||||
}
|
||||
|
||||
type blocklist struct {
|
||||
pathDir string
|
||||
sources []*SourceConfig
|
||||
blocklistRepository repository.BlocklistRepository
|
||||
logger log.Logger
|
||||
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
wg sync.WaitGroup
|
||||
|
||||
nftBlocklists map[string]block.Blocklist
|
||||
mu sync.Mutex
|
||||
|
||||
launchChannel chan updateSource
|
||||
}
|
||||
|
||||
func New(config Config, ctx context.Context, logger log.Logger) (Blocklist, error) {
|
||||
if config.PathDir == "" {
|
||||
return nil, fmt.Errorf("pathDir is empty")
|
||||
}
|
||||
|
||||
if err := filesystem.EnsureDir(config.PathDir); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &blocklist{
|
||||
pathDir: config.PathDir,
|
||||
sources: config.Sources,
|
||||
blocklistRepository: config.BlocklistRepository,
|
||||
logger: logger,
|
||||
ctx: ctx,
|
||||
|
||||
nftBlocklists: map[string]block.Blocklist{},
|
||||
mu: sync.Mutex{},
|
||||
|
||||
launchChannel: make(chan updateSource, 50),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *blocklist) Names() []string {
|
||||
var names []string
|
||||
for _, source := range b.sources {
|
||||
if source.Name != "" {
|
||||
names = append(names, source.Name)
|
||||
}
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
func (b *blocklist) NftReload(blocks map[string]block.Blocklist) error {
|
||||
b.logger.Debug("Reload blocklist")
|
||||
|
||||
b.mu.Lock()
|
||||
b.nftBlocklists = blocks
|
||||
b.mu.Unlock()
|
||||
|
||||
for _, source := range b.sources {
|
||||
if nftBlocklist, ok := b.nftBlocklists[source.Name]; ok {
|
||||
if listEntity, err := b.blocklistRepository.Get(source.Name); err != nil {
|
||||
b.logger.Error(fmt.Sprintf("Failed to get blocklist %s: %s", source.Name, err))
|
||||
} else if b.isFresh(source, listEntity) {
|
||||
file, err := b.pathFile(source)
|
||||
if err != nil {
|
||||
b.logger.Error(fmt.Sprintf("Failed to get blocklist file path: %s", err))
|
||||
continue
|
||||
}
|
||||
|
||||
if err := nftBlocklist.ReplaceElementsWithFile(file); err != nil {
|
||||
b.logger.Error(fmt.Sprintf("Failed to replace elements with file %s: %s", file, err))
|
||||
continue
|
||||
}
|
||||
}
|
||||
} else {
|
||||
b.logger.Error(fmt.Sprintf("NFTables sets blocklist %s not found", source.Name))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *blocklist) Run() {
|
||||
b.logger.Debug("Starting blocklist")
|
||||
if b.cancel != nil {
|
||||
// already started
|
||||
b.logger.Warn("Blocklist already started")
|
||||
return
|
||||
}
|
||||
b.ctx, b.cancel = context.WithCancel(b.ctx)
|
||||
go b.processUpdateData(b.ctx)
|
||||
|
||||
for _, src := range b.sources {
|
||||
if src == nil || src.Name == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
interval := src.Interval
|
||||
if interval <= 0 {
|
||||
interval = 5 * time.Minute // дефолт
|
||||
}
|
||||
|
||||
b.wg.Add(1)
|
||||
go b.runSourceWorker(src, interval)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *blocklist) runSourceWorker(sourceConfig *SourceConfig, interval time.Duration) {
|
||||
defer b.wg.Done()
|
||||
|
||||
b.launchChannel <- updateSource{
|
||||
forcedly: false,
|
||||
source: sourceConfig,
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-b.ctx.Done():
|
||||
b.logger.Debug(fmt.Sprintf("source %s stopped", sourceConfig.Name))
|
||||
return
|
||||
case <-ticker.C:
|
||||
b.logger.Debug(fmt.Sprintf("source %s tick", sourceConfig.Name))
|
||||
b.launchChannel <- updateSource{
|
||||
forcedly: true,
|
||||
source: sourceConfig,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *blocklist) processUpdateData(ctx context.Context) {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case updSource, ok := <-b.launchChannel:
|
||||
if !ok {
|
||||
// Channel closed
|
||||
return
|
||||
}
|
||||
|
||||
if updSource.forcedly {
|
||||
b.refreshSource(updSource.source)
|
||||
continue
|
||||
}
|
||||
|
||||
if listEntity, err := b.blocklistRepository.Get(updSource.source.Name); err != nil {
|
||||
b.logger.Error(fmt.Sprintf("Failed to get blocklist %s: %s", updSource.source.Name, err))
|
||||
continue
|
||||
} else if b.isFresh(updSource.source, listEntity) {
|
||||
b.logger.Debug(fmt.Sprintf("blocklist %s is fresh", updSource.source.Name))
|
||||
continue
|
||||
}
|
||||
|
||||
b.refreshSource(updSource.source)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *blocklist) refreshSource(sourceConfig *SourceConfig) {
|
||||
ipsV4, ipsV6, err := sourceConfig.Source.Get()
|
||||
if err != nil {
|
||||
b.logger.Error(fmt.Sprintf("Failed to get IPs from source %s: %s", sourceConfig.Name, err))
|
||||
return
|
||||
}
|
||||
|
||||
if nftBlocklist, ok := b.nftBlocklists[sourceConfig.Name]; ok {
|
||||
listEntity := &entity.Blocklist{
|
||||
UpdatedAtUnix: time.Now().Unix(),
|
||||
}
|
||||
|
||||
if err := filesystem.EnsureDir(b.pathDir); err != nil {
|
||||
b.logger.Error(fmt.Sprintf("Failed to ensure dir: %s", err))
|
||||
}
|
||||
file, err := b.pathFile(sourceConfig)
|
||||
if err != nil {
|
||||
b.logger.Error(fmt.Sprintf("Failed to get blocklist file path: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
if err := nftBlocklist.ReplaceElements(ipsV4, ipsV6, file); err != nil {
|
||||
b.logger.Error(fmt.Sprintf("Failed to replace elements: %s", err))
|
||||
}
|
||||
listEntity.Checksum, err = filesystem.FileChecksum(file)
|
||||
if err != nil {
|
||||
b.logger.Error(fmt.Sprintf("Failed to calculate checksum for %s: %s", file, err))
|
||||
return
|
||||
}
|
||||
|
||||
if err := b.blocklistRepository.Update(sourceConfig.Name, listEntity); err != nil {
|
||||
b.logger.Error(fmt.Sprintf("Failed to update blocklist %s: %s", sourceConfig.Name, err))
|
||||
}
|
||||
|
||||
} else {
|
||||
b.logger.Error(fmt.Sprintf("NFTables sets blocklist %s not found", sourceConfig.Name))
|
||||
return
|
||||
}
|
||||
|
||||
b.logger.Debug(fmt.Sprintf("refresh blocklist from %s", sourceConfig.Name))
|
||||
}
|
||||
|
||||
func (b *blocklist) Close() error {
|
||||
b.logger.Debug("Stopping blocklist")
|
||||
if b.cancel != nil {
|
||||
b.cancel()
|
||||
b.wg.Wait()
|
||||
b.cancel = nil
|
||||
}
|
||||
close(b.launchChannel)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *blocklist) pathFile(sourceConfig *SourceConfig) (string, error) {
|
||||
if sourceConfig == nil {
|
||||
return "", fmt.Errorf("sourceConfig is nil")
|
||||
}
|
||||
|
||||
if sourceConfig.Name == "" {
|
||||
return "", fmt.Errorf("sourceConfig.Name is empty")
|
||||
}
|
||||
|
||||
return strings.TrimRight(b.pathDir, "/") + "/" + sourceConfig.Name + ".nft", nil
|
||||
}
|
||||
|
||||
func (b *blocklist) isFresh(sourceConfig *SourceConfig, listEntity *entity.Blocklist) bool {
|
||||
if !listEntity.IsFresh(sourceConfig.Interval) {
|
||||
return false
|
||||
}
|
||||
|
||||
file, err := b.pathFile(sourceConfig)
|
||||
if err != nil {
|
||||
b.logger.Error(fmt.Sprintf("Failed to get blocklist file path: %s", err))
|
||||
return false
|
||||
}
|
||||
if !filesystem.FileExists(file) {
|
||||
b.logger.Warn(fmt.Sprintf("Blocklist file %s not found", file))
|
||||
return false
|
||||
}
|
||||
|
||||
fileChecksum, err := filesystem.FileChecksum(file)
|
||||
if err != nil {
|
||||
b.logger.Error(fmt.Sprintf("Failed to calculate checksum for %s: %s", file, err))
|
||||
return false
|
||||
}
|
||||
if listEntity.Checksum != fileChecksum {
|
||||
b.logger.Error(fmt.Sprintf("Blocklist file %s checksum is not equal to database checksum", file))
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package blocklist
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/blocklist/sources"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db/repository"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
BlocklistRepository repository.BlocklistRepository
|
||||
Sources []*SourceConfig
|
||||
PathDir string
|
||||
}
|
||||
|
||||
type SourceConfig struct {
|
||||
Name string
|
||||
Interval time.Duration
|
||||
Source sources.BlocklistSource
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package blocklist
|
||||
|
||||
import "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/block"
|
||||
|
||||
type FalseBlocklist struct {
|
||||
}
|
||||
|
||||
func NewFalseBlocklist() Blocklist {
|
||||
return &FalseBlocklist{}
|
||||
}
|
||||
|
||||
func (b *FalseBlocklist) Names() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (b *FalseBlocklist) NftReload(_ map[string]block.Blocklist) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *FalseBlocklist) Run() {}
|
||||
|
||||
func (b *FalseBlocklist) Close() error {
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package sources
|
||||
|
||||
import (
|
||||
"git.kor-elf.net/kor-elf-shield/blocklist"
|
||||
"git.kor-elf.net/kor-elf-shield/blocklist/parser"
|
||||
)
|
||||
|
||||
type BlocklistSource interface {
|
||||
Get() (ipV4 parser.IPs, ipV6 parser.IPs, err error)
|
||||
}
|
||||
|
||||
type blocklistSource struct {
|
||||
url string
|
||||
parser parser.Parser
|
||||
config blocklist.Config
|
||||
}
|
||||
|
||||
func NewBlocklistSource(url string, parser parser.Parser, config blocklist.Config) BlocklistSource {
|
||||
return &blocklistSource{
|
||||
url: url,
|
||||
parser: parser,
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *blocklistSource) Get() (ipV4 parser.IPs, ipV6 parser.IPs, err error) {
|
||||
return blocklist.GetSeparatedIPs(b.url, b.parser, b.config)
|
||||
}
|
||||
|
||||
type blocklistSourceZip struct {
|
||||
url string
|
||||
parser parser.Parser
|
||||
config blocklist.ConfigZip
|
||||
}
|
||||
|
||||
func NewBlocklistSourceZip(url string, parser parser.Parser, config blocklist.ConfigZip) BlocklistSource {
|
||||
return &blocklistSourceZip{
|
||||
url: url,
|
||||
parser: parser,
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *blocklistSourceZip) Get() (ipV4 parser.IPs, ipV6 parser.IPs, err error) {
|
||||
return blocklist.GetZipSeparatedIPs(b.url, b.parser, b.config)
|
||||
}
|
||||
@@ -5,19 +5,24 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/analyzer"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/blocklist"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/blocking"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/types"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/geoip"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/info"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/notifications"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/pidfile"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/socket"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/pkg/format"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/pkg/ip"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/setting/validate"
|
||||
)
|
||||
@@ -28,6 +33,7 @@ type Daemon interface {
|
||||
}
|
||||
|
||||
type daemon struct {
|
||||
info info.Info
|
||||
pidFile pidfile.PidFile
|
||||
socket socket.Socket
|
||||
logger log.Logger
|
||||
@@ -35,6 +41,8 @@ type daemon struct {
|
||||
notifications notifications.Notifications
|
||||
analyzer analyzer.Analyzer
|
||||
docker docker_monitor.Docker
|
||||
blocklist blocklist.Blocklist
|
||||
geoIPService geoip.GeoIP
|
||||
|
||||
stopCh chan struct{}
|
||||
}
|
||||
@@ -46,7 +54,7 @@ func (d *daemon) Run(ctx context.Context, isTesting bool, testingInterval uint16
|
||||
if err := d.socket.EnsureNoOtherProcess(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.firewall.Reload(); err != nil {
|
||||
if err := d.firewall.Reload(d.info); err != nil {
|
||||
d.firewall.ClearRules()
|
||||
return err
|
||||
}
|
||||
@@ -83,6 +91,16 @@ func (d *daemon) Run(ctx context.Context, isTesting bool, testingInterval uint16
|
||||
}()
|
||||
}
|
||||
|
||||
d.blocklist.Run()
|
||||
defer func() {
|
||||
_ = d.blocklist.Close()
|
||||
}()
|
||||
|
||||
d.geoIPService.Run(ctx)
|
||||
defer func() {
|
||||
_ = d.geoIPService.Close()
|
||||
}()
|
||||
|
||||
go d.socket.Run(ctx, d.socketCommand)
|
||||
d.runWorker(ctx, isTesting, testingInterval)
|
||||
|
||||
@@ -139,8 +157,35 @@ func (d *daemon) socketCommand(command string, args map[string]string, socket so
|
||||
case "stop":
|
||||
d.stopCh <- struct{}{}
|
||||
return socket.Write("ok")
|
||||
|
||||
case "status":
|
||||
return socket.Write("ok")
|
||||
var m runtime.MemStats
|
||||
runtime.ReadMemStats(&m)
|
||||
|
||||
text := fmt.Sprintf(
|
||||
"ok\n\n***\n"+
|
||||
"Version: %s\n"+
|
||||
"BuiltWith: %s\n"+
|
||||
"Uptime: %s\n"+
|
||||
"Goroutines: %d\n"+
|
||||
"Alloc: %s\n"+
|
||||
"HeapAlloc: %s\n"+
|
||||
"Sys: %s\n"+
|
||||
"HeapSys: %s\n"+
|
||||
"NumGC: %d\n"+
|
||||
"***\n",
|
||||
d.info.Version(),
|
||||
d.info.BuiltWith(),
|
||||
format.HumanDuration(d.info.Uptime()),
|
||||
runtime.NumGoroutine(),
|
||||
format.HumanBytes(m.Alloc), // Alloc is the total bytes of allocated heap objects.
|
||||
format.HumanBytes(m.HeapAlloc), // HeapAlloc is the total bytes of heap memory obtained from the OS.
|
||||
format.HumanBytes(m.Sys), // Sys is the total bytes of memory obtained from the OS.
|
||||
format.HumanBytes(m.HeapSys), // HeapSys is the total bytes of heap memory obtained from the OS.
|
||||
m.NumGC,
|
||||
)
|
||||
return socket.Write(text)
|
||||
|
||||
case "reopen_logger":
|
||||
if err := d.logger.ReOpen(); err != nil {
|
||||
_ = socket.Write("logger reopen failed: " + err.Error())
|
||||
@@ -203,6 +248,27 @@ func (d *daemon) socketCommand(command string, args map[string]string, socket so
|
||||
return err
|
||||
}
|
||||
return socket.Write("ok")
|
||||
|
||||
case "geoip_info":
|
||||
if args["ip"] == "" {
|
||||
return socket.Write("ip argument is required")
|
||||
}
|
||||
info, err := d.geoIPService.Info(args["ip"])
|
||||
if err != nil {
|
||||
_ = socket.Write("geoip info failed: " + err.Error())
|
||||
return err
|
||||
}
|
||||
return socket.Write(info)
|
||||
|
||||
case "geoip_refresh":
|
||||
ctx := context.Background()
|
||||
if err := d.geoIPService.Refresh(ctx); err != nil {
|
||||
_ = socket.Write("geoip refresh failed: " + err.Error())
|
||||
return err
|
||||
}
|
||||
_ = socket.Write("ok")
|
||||
return nil
|
||||
|
||||
default:
|
||||
_ = socket.Write("unknown command")
|
||||
return errors.New("unknown command")
|
||||
|
||||
@@ -19,6 +19,8 @@ type Repositories interface {
|
||||
AlertGroup() repository.AlertGroupRepository
|
||||
BruteForceProtectionGroup() repository.BruteForceProtectionGroupRepository
|
||||
Blocking() repository.BlockingRepository
|
||||
Blocklist() repository.BlocklistRepository
|
||||
Metadata() repository.MetadataRepository
|
||||
|
||||
Close() error
|
||||
}
|
||||
@@ -28,6 +30,8 @@ type repositories struct {
|
||||
alertGroup repository.AlertGroupRepository
|
||||
bruteForceProtectionGroup repository.BruteForceProtectionGroupRepository
|
||||
blocking repository.BlockingRepository
|
||||
blocklist repository.BlocklistRepository
|
||||
metadata repository.MetadataRepository
|
||||
|
||||
db []*bbolt.DB
|
||||
}
|
||||
@@ -57,6 +61,8 @@ func New(dataDir string) (Repositories, error) {
|
||||
alertGroup: repository.NewAlertGroupRepository(appDB),
|
||||
bruteForceProtectionGroup: repository.NewBruteForceProtectionGroupRepository(securityDB),
|
||||
blocking: repository.NewBlockingRepository(securityDB),
|
||||
blocklist: repository.NewBlocklistRepository(securityDB),
|
||||
metadata: repository.NewMetadataRepository(appDB),
|
||||
|
||||
db: []*bbolt.DB{appDB, securityDB},
|
||||
}, nil
|
||||
@@ -78,6 +84,14 @@ func (r *repositories) Blocking() repository.BlockingRepository {
|
||||
return r.blocking
|
||||
}
|
||||
|
||||
func (r *repositories) Blocklist() repository.BlocklistRepository {
|
||||
return r.blocklist
|
||||
}
|
||||
|
||||
func (r *repositories) Metadata() repository.MetadataRepository {
|
||||
return r.metadata
|
||||
}
|
||||
|
||||
func (r *repositories) Close() error {
|
||||
for _, db := range r.db {
|
||||
_ = db.Close()
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type Blocklist struct {
|
||||
UpdatedAtUnix int64 `json:"UpdateAtUnix"`
|
||||
Checksum string `json:"checksum"`
|
||||
}
|
||||
|
||||
// IsFresh returns true if the blocklist is fresh.
|
||||
func (b *Blocklist) IsFresh(interval time.Duration) bool {
|
||||
if b.Checksum == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
lastUpdate := time.Unix(b.UpdatedAtUnix, 0)
|
||||
return b.UpdatedAtUnix > 0 && time.Since(lastUpdate) <= interval
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package entity
|
||||
|
||||
const (
|
||||
MetadataKeyVersion = "Version"
|
||||
MetadataKeyFirewallFileNft = "firewall-file-nft" // checksum of the firewall file
|
||||
)
|
||||
|
||||
type Metadata struct {
|
||||
Value string `json:"Value"`
|
||||
}
|
||||
|
||||
func KeySetting(name string) string {
|
||||
return "setting-" + name
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db/entity"
|
||||
"go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
type BlocklistRepository interface {
|
||||
Get(name string) (*entity.Blocklist, error)
|
||||
Update(name string, entity *entity.Blocklist) error
|
||||
}
|
||||
|
||||
type blocklistRepository struct {
|
||||
db *bbolt.DB
|
||||
bucket string
|
||||
}
|
||||
|
||||
func NewBlocklistRepository(appDB *bbolt.DB) BlocklistRepository {
|
||||
return &blocklistRepository{
|
||||
db: appDB,
|
||||
bucket: blocklistBucket,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *blocklistRepository) Get(name string) (*entity.Blocklist, error) {
|
||||
blocklistEntity := &entity.Blocklist{}
|
||||
|
||||
err := r.db.View(func(tx *bbolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(r.bucket))
|
||||
if bucket == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
data := bucket.Get([]byte(name))
|
||||
if data == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return json.Unmarshal(data, blocklistEntity)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return blocklistEntity, err
|
||||
}
|
||||
|
||||
func (r *blocklistRepository) Update(name string, blocklistEntity *entity.Blocklist) error {
|
||||
return r.db.Update(func(tx *bbolt.Tx) error {
|
||||
b, err := tx.CreateBucketIfNotExists([]byte(r.bucket))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key := []byte(name)
|
||||
|
||||
data, err := json.Marshal(blocklistEntity)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return b.Put(key, data)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db/entity"
|
||||
"go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
type MetadataRepository interface {
|
||||
Get(name string) (*entity.Metadata, error)
|
||||
Update(name string, entity *entity.Metadata) error
|
||||
}
|
||||
|
||||
type metadataRepository struct {
|
||||
db *bbolt.DB
|
||||
bucket string
|
||||
}
|
||||
|
||||
func NewMetadataRepository(appDB *bbolt.DB) MetadataRepository {
|
||||
return &metadataRepository{
|
||||
db: appDB,
|
||||
bucket: metadataBucket,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *metadataRepository) Get(name string) (*entity.Metadata, error) {
|
||||
metadataEntity := &entity.Metadata{}
|
||||
|
||||
err := r.db.View(func(tx *bbolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(r.bucket))
|
||||
if bucket == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
data := bucket.Get([]byte(name))
|
||||
if data == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return json.Unmarshal(data, metadataEntity)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return metadataEntity, err
|
||||
}
|
||||
|
||||
func (r *metadataRepository) Update(name string, entity *entity.Metadata) error {
|
||||
return r.db.Update(func(tx *bbolt.Tx) error {
|
||||
b, err := tx.CreateBucketIfNotExists([]byte(r.bucket))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key := []byte(name)
|
||||
|
||||
data, err := json.Marshal(entity)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return b.Put(key, data)
|
||||
})
|
||||
}
|
||||
@@ -12,6 +12,8 @@ const (
|
||||
alertGroupBucket = "alert_group"
|
||||
bruteForceProtectionGroupBucket = "brute_force_protection_group"
|
||||
blockingBucket = "blocking"
|
||||
blocklistBucket = "blocklist"
|
||||
metadataBucket = "metadata"
|
||||
)
|
||||
|
||||
func nextID(b *bbolt.Bucket) ([]byte, error) {
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
package chain
|
||||
|
||||
import nftChain "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/chain"
|
||||
|
||||
type Chains interface {
|
||||
ForwardFilterJump(addRule func(expr ...string) error) error
|
||||
PreroutingFilterJump(addRule func(expr ...string) error) error
|
||||
|
||||
PreroutingNatJump(addRule func(expr ...string) error) error
|
||||
OutputNatJump(addRule func(expr ...string) error) error
|
||||
PostroutingNatJump(addRule func(expr ...string) error) error
|
||||
|
||||
List() *chains
|
||||
}
|
||||
|
||||
type chains struct {
|
||||
ForwardFilter Data
|
||||
ForwardBridge Data
|
||||
ForwardCT Data
|
||||
|
||||
PreroutingFilter Data
|
||||
DockerFilter Data
|
||||
DockerFilterFirst Data
|
||||
DockerFilterSecond Data
|
||||
|
||||
DockerNat Data
|
||||
PostroutingNat Data
|
||||
}
|
||||
|
||||
type Data struct {
|
||||
chain nftChain.Chain
|
||||
name string
|
||||
}
|
||||
|
||||
func (d *chains) ForwardFilterJump(addRule func(expr ...string) error) error {
|
||||
return d.ForwardFilter.Jump(addRule, "")
|
||||
}
|
||||
|
||||
func (d *chains) PreroutingFilterJump(addRule func(expr ...string) error) error {
|
||||
return d.PreroutingFilter.Jump(addRule, "")
|
||||
}
|
||||
|
||||
func (d *chains) PreroutingNatJump(addRule func(expr ...string) error) error {
|
||||
return d.DockerNat.Jump(addRule, "fib daddr type local counter")
|
||||
}
|
||||
|
||||
func (d *chains) OutputNatJump(addRule func(expr ...string) error) error {
|
||||
if err := d.DockerNat.Jump(addRule, "ip daddr != 127.0.0.0/8 fib daddr type local counter"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return d.DockerNat.Jump(addRule, "ip6 daddr != ::1 fib daddr type local counter")
|
||||
}
|
||||
|
||||
func (d *chains) PostroutingNatJump(addRule func(expr ...string) error) error {
|
||||
return d.PostroutingNat.Jump(addRule, "")
|
||||
}
|
||||
|
||||
func (d *chains) List() *chains {
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *Data) Jump(addRule func(expr ...string) error, rule string) error {
|
||||
args := []string{rule, "jump", d.name}
|
||||
return addRule(args...)
|
||||
}
|
||||
|
||||
func (d *Data) JumpTo(data *Data, rule string, comment string) error {
|
||||
args := []string{rule, "jump", d.name, comment}
|
||||
return data.AddRule(args...)
|
||||
}
|
||||
|
||||
func (d *Data) AddRule(rule ...string) error {
|
||||
return d.chain.AddRule(rule...)
|
||||
}
|
||||
|
||||
func (d *Data) RemoveRuleByHandle(handle uint64) error {
|
||||
return d.chain.RemoveRuleByHandle(handle)
|
||||
}
|
||||
|
||||
func (d *Data) ListRules() ([]nftChain.Rule, error) {
|
||||
return d.chain.ListRules()
|
||||
}
|
||||
|
||||
func (d *Data) Clear() error {
|
||||
return d.chain.Clear()
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package chain
|
||||
|
||||
type emptyChains struct {
|
||||
}
|
||||
|
||||
func NewEmptyChains() Chains {
|
||||
return &emptyChains{}
|
||||
}
|
||||
|
||||
func (c *emptyChains) ForwardFilterJump(_ func(expr ...string) error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *emptyChains) PreroutingFilterJump(_ func(expr ...string) error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *emptyChains) PreroutingNatJump(_ func(expr ...string) error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *emptyChains) OutputNatJump(_ func(expr ...string) error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *emptyChains) PostroutingNatJump(_ func(expr ...string) error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *emptyChains) List() *chains {
|
||||
return &chains{}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
package chain
|
||||
|
||||
import nftChain "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/chain"
|
||||
|
||||
func NewChains(newNoneChain func(chain string) (nftChain.Chain, error)) (Chains, error) {
|
||||
chainsData := &chains{}
|
||||
|
||||
if data, err := newChainData("docker_nat", newNoneChain); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
chainsData.DockerNat = data
|
||||
}
|
||||
|
||||
if data, err := newChainData("docker_postrouting_nat", newNoneChain); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
chainsData.PostroutingNat = data
|
||||
}
|
||||
|
||||
if data, err := newChainData("docker_prerouting_filter", newNoneChain); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
chainsData.PreroutingFilter = data
|
||||
}
|
||||
|
||||
if data, err := newChainData("docker_filter", newNoneChain); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
chainsData.DockerFilter = data
|
||||
}
|
||||
|
||||
if data, err := newChainData("docker_filter_first", newNoneChain); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
chainsData.DockerFilterFirst = data
|
||||
}
|
||||
|
||||
if data, err := newChainData("docker_filter_second", newNoneChain); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
chainsData.DockerFilterSecond = data
|
||||
}
|
||||
|
||||
if data, err := newChainData("docker_forward_filter", newNoneChain); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
chainsData.ForwardFilter = data
|
||||
}
|
||||
|
||||
if data, err := newChainData("docker_forward_bridge", newNoneChain); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
chainsData.ForwardBridge = data
|
||||
}
|
||||
|
||||
if data, err := newChainData("docker_forward_ct", newNoneChain); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
chainsData.ForwardCT = data
|
||||
}
|
||||
|
||||
return chainsData, nil
|
||||
}
|
||||
|
||||
func newChainData(chainName string, newNoneChain func(chain string) (nftChain.Chain, error)) (Data, error) {
|
||||
data := Data{
|
||||
name: chainName,
|
||||
}
|
||||
|
||||
newChain, err := newNoneChain(data.name)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
|
||||
data.chain = newChain
|
||||
return data, nil
|
||||
}
|
||||
@@ -3,16 +3,14 @@ package docker_monitor
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/client"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/firewall"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/rule_strategy"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
)
|
||||
|
||||
type Docker interface {
|
||||
NftReload(newNoneChain func(chain string) (nftChain.Chain, error)) error
|
||||
NftChains() chain.Chains
|
||||
NftReload(nftDocker firewall.NFTDocker) error
|
||||
Run()
|
||||
Close() error
|
||||
}
|
||||
@@ -39,12 +37,8 @@ func New(config *Config, ctx context.Context, logger log.Logger) (Docker, error)
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *docker) NftReload(newNoneChain func(chain string) (nftChain.Chain, error)) error {
|
||||
return d.ruleStrategy.Reload(newNoneChain)
|
||||
}
|
||||
|
||||
func (d *docker) NftChains() chain.Chains {
|
||||
return d.ruleStrategy.Chains()
|
||||
func (d *docker) NftReload(nftDocker firewall.NFTDocker) error {
|
||||
return d.ruleStrategy.Reload(nftDocker)
|
||||
}
|
||||
|
||||
func (d *docker) Run() {
|
||||
@@ -66,9 +60,3 @@ func (d *docker) Run() {
|
||||
func (d *docker) Close() error {
|
||||
return d.dockerClient.EventsClose()
|
||||
}
|
||||
|
||||
func (d *docker) chainCommand(chainData chain.Data, rule string) {
|
||||
if err := chainData.AddRule(rule); err != nil {
|
||||
d.logger.Error(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +1,23 @@
|
||||
package docker_monitor
|
||||
|
||||
import (
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/chain"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/firewall"
|
||||
)
|
||||
|
||||
type DockerNotSupport struct {
|
||||
chains chain.Chains
|
||||
}
|
||||
type dockerNotSupport struct{}
|
||||
|
||||
func NewDockerNotSupport() Docker {
|
||||
return &DockerNotSupport{
|
||||
chains: chain.NewEmptyChains(),
|
||||
}
|
||||
return &dockerNotSupport{}
|
||||
}
|
||||
|
||||
func (d *DockerNotSupport) NftReload(_ func(chain string) (nftChain.Chain, error)) error {
|
||||
func (d *dockerNotSupport) NftReload(_ firewall.NFTDocker) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DockerNotSupport) NftChains() chain.Chains {
|
||||
return d.chains
|
||||
}
|
||||
|
||||
func (d *DockerNotSupport) Run() {
|
||||
func (d *dockerNotSupport) Run() {
|
||||
|
||||
}
|
||||
|
||||
func (d *DockerNotSupport) Close() error {
|
||||
func (d *dockerNotSupport) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
package firewall
|
||||
|
||||
import (
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
nftFirewall "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/chain"
|
||||
)
|
||||
|
||||
type NFTDocker interface {
|
||||
Chains() NFTDockerChains
|
||||
NFT() nftFirewall.NFT
|
||||
}
|
||||
|
||||
type NFTDockerChains interface {
|
||||
List() []chain.Docker
|
||||
|
||||
ForwardFilter() chain.Docker
|
||||
ForwardBridge() chain.Docker
|
||||
ForwardCT() chain.Docker
|
||||
|
||||
PreroutingFilter() chain.Docker
|
||||
DockerFilter() chain.Docker
|
||||
DockerFilterFirst() chain.Docker
|
||||
DockerFilterSecond() chain.Docker
|
||||
|
||||
DockerNat() chain.Docker
|
||||
PostroutingNat() chain.Docker
|
||||
}
|
||||
|
||||
type nftDocker struct {
|
||||
chains NFTDockerChains
|
||||
nft nftFirewall.NFT
|
||||
}
|
||||
|
||||
func NewNFT(nft nftFirewall.NFT, chains NFTDockerChains) NFTDocker {
|
||||
return &nftDocker{
|
||||
chains: chains,
|
||||
nft: nft,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *nftDocker) NFT() nftFirewall.NFT {
|
||||
return n.nft
|
||||
}
|
||||
|
||||
func (n *nftDocker) Chains() NFTDockerChains {
|
||||
return n.chains
|
||||
}
|
||||
|
||||
type nftDockerChains struct {
|
||||
forwardFilter chain.Docker
|
||||
forwardBridge chain.Docker
|
||||
forwardCT chain.Docker
|
||||
|
||||
preroutingFilter chain.Docker
|
||||
dockerFilter chain.Docker
|
||||
dockerFilterFirst chain.Docker
|
||||
dockerFilterSecond chain.Docker
|
||||
|
||||
dockerNat chain.Docker
|
||||
postroutingNat chain.Docker
|
||||
}
|
||||
|
||||
func NewNFTChains(nft nftFirewall.NFT, family family.Type, table string) NFTDockerChains {
|
||||
return &nftDockerChains{
|
||||
forwardFilter: chain.NewDocker(nft.NFT(), family, table, "docker_forward_filter"),
|
||||
forwardBridge: chain.NewDocker(nft.NFT(), family, table, "docker_forward_bridge"),
|
||||
forwardCT: chain.NewDocker(nft.NFT(), family, table, "docker_forward_ct"),
|
||||
|
||||
preroutingFilter: chain.NewDocker(nft.NFT(), family, table, "docker_prerouting_filter"),
|
||||
dockerFilter: chain.NewDocker(nft.NFT(), family, table, "docker_filter"),
|
||||
dockerFilterFirst: chain.NewDocker(nft.NFT(), family, table, "docker_filter_first"),
|
||||
dockerFilterSecond: chain.NewDocker(nft.NFT(), family, table, "docker_filter_second"),
|
||||
|
||||
dockerNat: chain.NewDocker(nft.NFT(), family, table, "docker_nat"),
|
||||
postroutingNat: chain.NewDocker(nft.NFT(), family, table, "docker_postrouting_nat"),
|
||||
}
|
||||
}
|
||||
|
||||
func (n *nftDockerChains) ForwardFilter() chain.Docker {
|
||||
return n.forwardFilter
|
||||
}
|
||||
|
||||
func (n *nftDockerChains) ForwardBridge() chain.Docker {
|
||||
return n.forwardBridge
|
||||
}
|
||||
|
||||
func (n *nftDockerChains) ForwardCT() chain.Docker {
|
||||
return n.forwardCT
|
||||
}
|
||||
|
||||
func (n *nftDockerChains) PreroutingFilter() chain.Docker {
|
||||
return n.preroutingFilter
|
||||
}
|
||||
|
||||
func (n *nftDockerChains) DockerFilter() chain.Docker {
|
||||
return n.dockerFilter
|
||||
}
|
||||
|
||||
func (n *nftDockerChains) DockerFilterFirst() chain.Docker {
|
||||
return n.dockerFilterFirst
|
||||
}
|
||||
|
||||
func (n *nftDockerChains) DockerFilterSecond() chain.Docker {
|
||||
return n.dockerFilterSecond
|
||||
}
|
||||
|
||||
func (n *nftDockerChains) DockerNat() chain.Docker {
|
||||
return n.dockerNat
|
||||
}
|
||||
|
||||
func (n *nftDockerChains) PostroutingNat() chain.Docker {
|
||||
return n.postroutingNat
|
||||
}
|
||||
|
||||
func (n *nftDockerChains) List() []chain.Docker {
|
||||
return []chain.Docker{
|
||||
n.forwardFilter,
|
||||
n.forwardBridge,
|
||||
n.forwardCT,
|
||||
n.preroutingFilter,
|
||||
n.dockerFilter,
|
||||
n.dockerFilterFirst,
|
||||
n.dockerFilterSecond,
|
||||
n.dockerNat,
|
||||
n.postroutingNat,
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ func newRuleStrategy(config *Config, dockerClient client.Docker, logger log.Logg
|
||||
|
||||
switch config.RuleStrategy {
|
||||
case RuleStrategyRebuild:
|
||||
return rule_strategy.NewRebuildStrategy(generate), nil
|
||||
return rule_strategy.NewRebuildStrategy(generate, logger), nil
|
||||
case RuleStrategyIncremental:
|
||||
return rule_strategy.NewIncrementalStrategy(generate, dockerClient, logger), nil
|
||||
}
|
||||
|
||||
@@ -3,17 +3,19 @@ package rule_strategy
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/chain"
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/client"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/firewall"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
)
|
||||
|
||||
type Generator interface {
|
||||
GenerateAll(chains chain.Chains, isComment bool)
|
||||
GenerateBridge(bridge client.Bridge, chain chain.Chains, isComment bool)
|
||||
GenerateContainer(container client.Container, bridgeName string, chain chain.Chains, isComment bool)
|
||||
ClearChains(chains chain.Chains)
|
||||
AddRule(chainData chain.Data, rule string)
|
||||
GenerateAll(builder nft.BatchBuilder, chains firewall.NFTDockerChains, isComment bool)
|
||||
GenerateBridge(bridge client.Bridge, builder nft.BatchBuilder, chains firewall.NFTDockerChains, isComment bool)
|
||||
GenerateContainer(container client.Container, bridgeName string, builder nft.BatchBuilder, chains firewall.NFTDockerChains, isComment bool)
|
||||
ClearChains(builder nft.BatchBuilder, chains firewall.NFTDockerChains)
|
||||
AddRule(builder nft.BatchBuilder, chainDocker chain.Docker, rule string)
|
||||
}
|
||||
|
||||
type generator struct {
|
||||
@@ -28,19 +30,17 @@ func NewGenerator(dockerClient client.Docker, logger log.Logger) Generator {
|
||||
}
|
||||
}
|
||||
|
||||
func (g *generator) GenerateAll(chains chain.Chains, isComment bool) {
|
||||
listChains := chains.List()
|
||||
|
||||
if err := listChains.ForwardCT.JumpTo(&listChains.ForwardFilter, "", ""); err != nil {
|
||||
func (g *generator) GenerateAll(builder nft.BatchBuilder, chains firewall.NFTDockerChains, isComment bool) {
|
||||
if err := chains.ForwardCT().JumpTo(builder, chains.ForwardFilter(), "", ""); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
if err := listChains.ForwardBridge.JumpTo(&listChains.ForwardFilter, "", ""); err != nil {
|
||||
if err := chains.ForwardBridge().JumpTo(builder, chains.ForwardFilter(), "", ""); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
if err := listChains.DockerFilterFirst.JumpTo(&listChains.DockerFilter, "", ""); err != nil {
|
||||
if err := chains.DockerFilterFirst().JumpTo(builder, chains.DockerFilter(), "", ""); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
if err := listChains.DockerFilterSecond.JumpTo(&listChains.DockerFilter, "", ""); err != nil {
|
||||
if err := chains.DockerFilterSecond().JumpTo(builder, chains.DockerFilter(), "", ""); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
|
||||
@@ -51,20 +51,18 @@ func (g *generator) GenerateAll(chains chain.Chains, isComment bool) {
|
||||
}
|
||||
|
||||
for _, bridge := range bridges {
|
||||
g.GenerateBridge(bridge, chains, isComment)
|
||||
g.GenerateBridge(bridge, builder, chains, isComment)
|
||||
|
||||
if bridge.Containers == nil {
|
||||
continue
|
||||
}
|
||||
for _, container := range bridge.Containers {
|
||||
g.GenerateContainer(container, bridge.Name, chains, isComment)
|
||||
g.GenerateContainer(container, bridge.Name, builder, chains, isComment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *generator) GenerateBridge(bridge client.Bridge, chain chain.Chains, isComment bool) {
|
||||
listChains := chain.List()
|
||||
|
||||
func (g *generator) GenerateBridge(bridge client.Bridge, builder nft.BatchBuilder, chains firewall.NFTDockerChains, isComment bool) {
|
||||
var rule string
|
||||
comment := ""
|
||||
if isComment {
|
||||
@@ -72,27 +70,26 @@ func (g *generator) GenerateBridge(bridge client.Bridge, chain chain.Chains, isC
|
||||
}
|
||||
|
||||
rule = fmt.Sprintf("iifname != \"%s\" oifname \"%s\" counter drop %s", bridge.Name, bridge.Name, comment)
|
||||
g.AddRule(listChains.DockerFilterSecond, rule)
|
||||
g.AddRule(builder, chains.DockerFilterSecond(), rule)
|
||||
|
||||
rule = fmt.Sprintf("iifname \"%s\" counter accept %s", bridge.Name, comment)
|
||||
g.AddRule(listChains.ForwardFilter, rule)
|
||||
g.AddRule(builder, chains.ForwardFilter(), rule)
|
||||
|
||||
rule = fmt.Sprintf("oifname \"%s\" counter", bridge.Name)
|
||||
if err := listChains.DockerFilter.JumpTo(&listChains.ForwardBridge, rule, comment); err != nil {
|
||||
if err := chains.DockerFilter().JumpTo(builder, chains.ForwardBridge(), rule, comment); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
|
||||
rule = fmt.Sprintf("oifname \"%s\" ct state related,established counter accept %s", bridge.Name, comment)
|
||||
g.AddRule(listChains.ForwardCT, rule)
|
||||
g.AddRule(builder, chains.ForwardCT(), rule)
|
||||
|
||||
for _, subnet := range bridge.Subnets {
|
||||
rule = fmt.Sprintf("ip saddr %s oifname != \"%s\" counter masquerade %s", subnet, bridge.Name, comment)
|
||||
g.AddRule(listChains.PostroutingNat, rule)
|
||||
g.AddRule(builder, chains.PostroutingNat(), rule)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *generator) GenerateContainer(container client.Container, bridgeName string, chain chain.Chains, isComment bool) {
|
||||
listChains := chain.List()
|
||||
func (g *generator) GenerateContainer(container client.Container, bridgeName string, builder nft.BatchBuilder, chains firewall.NFTDockerChains, isComment bool) {
|
||||
var rule string
|
||||
comment := ""
|
||||
if isComment {
|
||||
@@ -101,14 +98,14 @@ func (g *generator) GenerateContainer(container client.Container, bridgeName str
|
||||
|
||||
for _, ipInfo := range container.Networks.IPAddresses {
|
||||
rule = fmt.Sprintf("%s daddr %s iifname != \"%s\" counter drop %s", ipInfo.NftPrefix(), ipInfo.Address, bridgeName, comment)
|
||||
g.AddRule(listChains.PreroutingFilter, rule)
|
||||
g.AddRule(builder, chains.PreroutingFilter(), rule)
|
||||
|
||||
for _, port := range container.Networks.Ports {
|
||||
isZeroAddress := false
|
||||
for _, hostInfo := range port.HostPort {
|
||||
if hostInfo.IP.Address != "0.0.0.0" && hostInfo.IP.Address != "::" && (hostInfo.IP.Address == "127.0.0.1" || hostInfo.IP.Address == "::1") {
|
||||
rule = fmt.Sprintf("%s daddr %s iifname != \"lo\" %s dport %s counter drop %s", hostInfo.IP.NftPrefix(), hostInfo.IP.Address, port.Protocol, hostInfo.Port, comment)
|
||||
g.AddRule(listChains.PreroutingFilter, rule)
|
||||
g.AddRule(builder, chains.PreroutingFilter(), rule)
|
||||
}
|
||||
|
||||
if hostInfo.IP.Address == "0.0.0.0" || hostInfo.IP.Address == "::" {
|
||||
@@ -117,58 +114,32 @@ func (g *generator) GenerateContainer(container client.Container, bridgeName str
|
||||
}
|
||||
isZeroAddress = true
|
||||
rule = fmt.Sprintf("iifname != \"%s\" %s dport %s counter dnat %s to %s:%s %s", bridgeName, port.Protocol, hostInfo.Port, ipInfo.NftPrefix(), ipInfo.Address, port.Port, comment)
|
||||
g.AddRule(listChains.DockerNat, rule)
|
||||
g.AddRule(builder, chains.DockerNat(), rule)
|
||||
|
||||
rule = fmt.Sprintf("%s daddr %s iifname != \"%s\" oifname \"%s\" %s dport %s counter accept %s", ipInfo.NftPrefix(), ipInfo.Address, bridgeName, bridgeName, port.Protocol, port.Port, comment)
|
||||
g.AddRule(listChains.DockerFilterFirst, rule)
|
||||
g.AddRule(builder, chains.DockerFilterFirst(), rule)
|
||||
continue
|
||||
}
|
||||
rule = fmt.Sprintf("%s daddr %s iifname != \"%s\" oifname \"%s\" %s dport %s counter accept %s", ipInfo.NftPrefix(), ipInfo.Address, bridgeName, bridgeName, port.Protocol, port.Port, comment)
|
||||
g.AddRule(listChains.DockerFilterFirst, rule)
|
||||
g.AddRule(builder, chains.DockerFilterFirst(), rule)
|
||||
|
||||
rule = fmt.Sprintf("%s daddr %s iifname != \"%s\" %s dport %s counter dnat to %s:%s %s", hostInfo.IP.NftPrefix(), hostInfo.IP.Address, bridgeName, port.Protocol, hostInfo.Port, ipInfo.Address, port.Port, comment)
|
||||
g.AddRule(listChains.DockerNat, rule)
|
||||
g.AddRule(builder, chains.DockerNat(), rule)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *generator) ClearChains(chains chain.Chains) {
|
||||
listChains := chains.List()
|
||||
|
||||
if err := listChains.DockerNat.Clear(); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
if err := listChains.PostroutingNat.Clear(); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
if err := listChains.PreroutingFilter.Clear(); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
if err := listChains.DockerFilter.Clear(); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
if err := listChains.DockerFilterFirst.Clear(); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
if err := listChains.DockerFilterSecond.Clear(); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
if err := listChains.ForwardFilter.Clear(); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
|
||||
if err := listChains.ForwardBridge.Clear(); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
|
||||
if err := listChains.ForwardCT.Clear(); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
func (g *generator) ClearChains(builder nft.BatchBuilder, chains firewall.NFTDockerChains) {
|
||||
for _, chain := range chains.List() {
|
||||
if err := chain.Clear(builder); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *generator) AddRule(chainData chain.Data, rule string) {
|
||||
if err := chainData.AddRule(rule); err != nil {
|
||||
func (g *generator) AddRule(builder nft.BatchBuilder, chainDocker chain.Docker, rule string) {
|
||||
if err := chainDocker.AddRule(builder, rule); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package rule_strategy
|
||||
|
||||
import (
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/client"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/firewall"
|
||||
)
|
||||
|
||||
type Strategy interface {
|
||||
Reload(newNoneChain func(chain string) (nftChain.Chain, error)) error
|
||||
Chains() chain.Chains
|
||||
Reload(nftDocker firewall.NFTDocker) error
|
||||
Chains() firewall.NFTDockerChains
|
||||
Event(event *client.Event)
|
||||
}
|
||||
|
||||
@@ -3,15 +3,16 @@ package rule_strategy
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/chain"
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/client"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/firewall"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
)
|
||||
|
||||
type incrementalStrategy struct {
|
||||
dockerClient client.Docker
|
||||
chains chain.Chains
|
||||
nftDocker firewall.NFTDocker
|
||||
generator Generator
|
||||
logger log.Logger
|
||||
}
|
||||
@@ -24,20 +25,26 @@ func NewIncrementalStrategy(generator Generator, dockerClient client.Docker, log
|
||||
}
|
||||
}
|
||||
|
||||
func (i *incrementalStrategy) Reload(newNoneChain func(chain string) (nftChain.Chain, error)) error {
|
||||
chains, err := chain.NewChains(newNoneChain)
|
||||
func (i *incrementalStrategy) Reload(nftDocker firewall.NFTDocker) error {
|
||||
i.nftDocker = nftDocker
|
||||
|
||||
batchBuilder, err := i.nftDocker.NFT().NewBuildBatch()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.chains = chains
|
||||
defer func() {
|
||||
if err := batchBuilder.Close(); err != nil {
|
||||
i.logger.Warn(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
i.generator.GenerateAll(i.chains, true)
|
||||
i.generator.GenerateAll(batchBuilder, i.nftDocker.Chains(), true)
|
||||
|
||||
return nil
|
||||
return i.nftDocker.NFT().RunBatch(batchBuilder)
|
||||
}
|
||||
|
||||
func (i *incrementalStrategy) Chains() chain.Chains {
|
||||
return i.chains
|
||||
func (i *incrementalStrategy) Chains() firewall.NFTDockerChains {
|
||||
return i.nftDocker.Chains()
|
||||
}
|
||||
|
||||
func (i *incrementalStrategy) Event(event *client.Event) {
|
||||
@@ -54,7 +61,9 @@ func (i *incrementalStrategy) Event(event *client.Event) {
|
||||
}
|
||||
|
||||
if event.Action == "die" {
|
||||
i.eventContainerStop(event.ID)
|
||||
if err := i.eventContainerStop(event.ID); err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to handle container stop event: %s", err))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -66,10 +75,14 @@ func (i *incrementalStrategy) Event(event *client.Event) {
|
||||
if err := i.eventNetworkCreate(event.ID); err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to handle network create event: %s", err))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if event.Action == "destroy" {
|
||||
i.eventNetworkDestroy(event.ID)
|
||||
if err := i.eventNetworkDestroy(event.ID); err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to handle network destroy event: %s", err))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
@@ -82,35 +95,55 @@ func (i *incrementalStrategy) eventContainerStart(containerId string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
batchBuilder, err := i.nftDocker.NFT().NewBuildBatch()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err := batchBuilder.Close(); err != nil {
|
||||
i.logger.Warn(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
for _, ipInfo := range container.Networks.IPAddresses {
|
||||
bridge, err := i.dockerClient.FetchBridge(ipInfo.NetworkID)
|
||||
if err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to fetch bridge for container %s: %s", containerId, err))
|
||||
continue
|
||||
}
|
||||
i.generator.GenerateContainer(container, bridge.Name, i.chains, true)
|
||||
i.generator.GenerateContainer(container, bridge.Name, batchBuilder, i.nftDocker.Chains(), true)
|
||||
}
|
||||
|
||||
return nil
|
||||
return i.nftDocker.NFT().RunBatch(batchBuilder)
|
||||
}
|
||||
|
||||
func (i *incrementalStrategy) eventContainerStop(containerId string) {
|
||||
listChains := i.chains.List()
|
||||
func (i *incrementalStrategy) eventContainerStop(containerId string) error {
|
||||
batchBuilder, err := i.nftDocker.NFT().NewBuildBatch()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err := batchBuilder.Close(); err != nil {
|
||||
i.logger.Warn(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if err := i.nftRuleDeleteContainer(containerId, &listChains.PreroutingFilter); err != nil {
|
||||
if err := i.nftRuleDeleteContainer(containerId, batchBuilder, i.nftDocker.Chains().PreroutingFilter()); err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to delete container %s rules: %s", containerId, err))
|
||||
}
|
||||
|
||||
if err := i.nftRuleDeleteContainer(containerId, &listChains.DockerNat); err != nil {
|
||||
if err := i.nftRuleDeleteContainer(containerId, batchBuilder, i.nftDocker.Chains().DockerNat()); err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to delete container %s rules: %s", containerId, err))
|
||||
}
|
||||
|
||||
if err := i.nftRuleDeleteContainer(containerId, &listChains.DockerFilterFirst); err != nil {
|
||||
if err := i.nftRuleDeleteContainer(containerId, batchBuilder, i.nftDocker.Chains().DockerFilterFirst()); err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to delete container %s rules: %s", containerId, err))
|
||||
}
|
||||
|
||||
return i.nftDocker.NFT().RunBatch(batchBuilder)
|
||||
}
|
||||
|
||||
func (i *incrementalStrategy) nftRuleDeleteContainer(containerId string, chain *chain.Data) error {
|
||||
func (i *incrementalStrategy) nftRuleDeleteContainer(containerId string, builder nft.BatchBuilder, chain chain.Docker) error {
|
||||
rules, err := chain.ListRules()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -120,7 +153,7 @@ func (i *incrementalStrategy) nftRuleDeleteContainer(containerId string, chain *
|
||||
if rule.Comment != "container_id:"+containerId {
|
||||
continue
|
||||
}
|
||||
if err := chain.RemoveRuleByHandle(rule.Handle); err != nil {
|
||||
if err := chain.RemoveRuleByHandle(builder, rule.Handle); err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to delete container %s rule: %s", containerId, err))
|
||||
}
|
||||
}
|
||||
@@ -129,40 +162,60 @@ func (i *incrementalStrategy) nftRuleDeleteContainer(containerId string, chain *
|
||||
}
|
||||
|
||||
func (i *incrementalStrategy) eventNetworkCreate(bridgeId string) error {
|
||||
batchBuilder, err := i.nftDocker.NFT().NewBuildBatch()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err := batchBuilder.Close(); err != nil {
|
||||
i.logger.Warn(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
bridge, err := i.dockerClient.FetchBridge(bridgeId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
i.generator.GenerateBridge(bridge, i.chains, true)
|
||||
return nil
|
||||
i.generator.GenerateBridge(bridge, batchBuilder, i.nftDocker.Chains(), true)
|
||||
return i.nftDocker.NFT().RunBatch(batchBuilder)
|
||||
}
|
||||
|
||||
func (i *incrementalStrategy) eventNetworkDestroy(bridgeId string) {
|
||||
listChains := i.chains.List()
|
||||
func (i *incrementalStrategy) eventNetworkDestroy(bridgeId string) error {
|
||||
batchBuilder, err := i.nftDocker.NFT().NewBuildBatch()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err := batchBuilder.Close(); err != nil {
|
||||
i.logger.Warn(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if err := i.nftRuleDeleteBridge(bridgeId, &listChains.DockerFilterSecond); err != nil {
|
||||
if err := i.nftRuleDeleteBridge(bridgeId, batchBuilder, i.nftDocker.Chains().DockerFilterSecond()); err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to delete bridge %s rules: %s", bridgeId, err))
|
||||
}
|
||||
|
||||
if err := i.nftRuleDeleteBridge(bridgeId, &listChains.ForwardFilter); err != nil {
|
||||
if err := i.nftRuleDeleteBridge(bridgeId, batchBuilder, i.nftDocker.Chains().ForwardFilter()); err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to delete bridge %s rules: %s", bridgeId, err))
|
||||
}
|
||||
|
||||
if err := i.nftRuleDeleteBridge(bridgeId, &listChains.ForwardBridge); err != nil {
|
||||
if err := i.nftRuleDeleteBridge(bridgeId, batchBuilder, i.nftDocker.Chains().ForwardBridge()); err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to delete bridge %s rules: %s", bridgeId, err))
|
||||
}
|
||||
|
||||
if err := i.nftRuleDeleteBridge(bridgeId, &listChains.ForwardCT); err != nil {
|
||||
if err := i.nftRuleDeleteBridge(bridgeId, batchBuilder, i.nftDocker.Chains().ForwardCT()); err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to delete bridge %s rules: %s", bridgeId, err))
|
||||
}
|
||||
|
||||
if err := i.nftRuleDeleteBridge(bridgeId, &listChains.PostroutingNat); err != nil {
|
||||
if err := i.nftRuleDeleteBridge(bridgeId, batchBuilder, i.nftDocker.Chains().PostroutingNat()); err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to delete bridge %s rules: %s", bridgeId, err))
|
||||
}
|
||||
|
||||
return i.nftDocker.NFT().RunBatch(batchBuilder)
|
||||
}
|
||||
|
||||
func (i *incrementalStrategy) nftRuleDeleteBridge(bridgeId string, chain *chain.Data) error {
|
||||
func (i *incrementalStrategy) nftRuleDeleteBridge(bridgeId string, builder nft.BatchBuilder, chain chain.Docker) error {
|
||||
rules, err := chain.ListRules()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -172,7 +225,7 @@ func (i *incrementalStrategy) nftRuleDeleteBridge(bridgeId string, chain *chain.
|
||||
if rule.Comment != "bridge_id:"+bridgeId {
|
||||
continue
|
||||
}
|
||||
if err := chain.RemoveRuleByHandle(rule.Handle); err != nil {
|
||||
if err := chain.RemoveRuleByHandle(builder, rule.Handle); err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to delete bridge %s rule: %s", bridgeId, err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,44 @@
|
||||
package rule_strategy
|
||||
|
||||
import (
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/client"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/firewall"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
)
|
||||
|
||||
type rebuildStrategy struct {
|
||||
chains chain.Chains
|
||||
nftDocker firewall.NFTDocker
|
||||
generator Generator
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
func NewRebuildStrategy(generator Generator) Strategy {
|
||||
func NewRebuildStrategy(generator Generator, logger log.Logger) Strategy {
|
||||
return &rebuildStrategy{
|
||||
generator: generator,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *rebuildStrategy) Reload(newNoneChain func(chain string) (nftChain.Chain, error)) error {
|
||||
chains, err := chain.NewChains(newNoneChain)
|
||||
func (r *rebuildStrategy) Reload(nftDocker firewall.NFTDocker) error {
|
||||
r.nftDocker = nftDocker
|
||||
|
||||
batchBuilder, err := r.nftDocker.NFT().NewBuildBatch()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.chains = chains
|
||||
defer func() {
|
||||
if err := batchBuilder.Close(); err != nil {
|
||||
r.logger.Warn(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
r.generator.GenerateAll(r.chains, false)
|
||||
r.generator.GenerateAll(batchBuilder, r.nftDocker.Chains(), false)
|
||||
|
||||
return nil
|
||||
return r.nftDocker.NFT().RunBatch(batchBuilder)
|
||||
}
|
||||
|
||||
func (r *rebuildStrategy) Chains() chain.Chains {
|
||||
return r.chains
|
||||
func (r *rebuildStrategy) Chains() firewall.NFTDockerChains {
|
||||
return r.nftDocker.Chains()
|
||||
}
|
||||
|
||||
func (r *rebuildStrategy) Event(event *client.Event) {
|
||||
@@ -38,6 +46,21 @@ func (r *rebuildStrategy) Event(event *client.Event) {
|
||||
return
|
||||
}
|
||||
|
||||
r.generator.ClearChains(r.chains)
|
||||
r.generator.GenerateAll(r.chains, false)
|
||||
batchBuilder, err := r.nftDocker.NFT().NewBuildBatch()
|
||||
if err != nil {
|
||||
r.logger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err := batchBuilder.Close(); err != nil {
|
||||
r.logger.Warn(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
r.generator.ClearChains(batchBuilder, r.nftDocker.Chains())
|
||||
r.generator.GenerateAll(batchBuilder, r.nftDocker.Chains(), false)
|
||||
|
||||
if err := r.nftDocker.NFT().RunBatch(batchBuilder); err != nil {
|
||||
r.logger.Error(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,13 +9,14 @@ import (
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db/entity"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db/repository"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/chain/block"
|
||||
nftFirewall "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/block"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/types"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
)
|
||||
|
||||
type API interface {
|
||||
NftReload(blockListIP block.ListIP, blockListIPWithPort block.ListIPWithPort) error
|
||||
NftReload(nft nftFirewall.NFT, blockListIP block.ListIP, blockListIPWithPort block.ListIPWithPort) error
|
||||
BlockIP(block BlockIP) (bool, error)
|
||||
BlockIPWithPorts(block BlockIPWithPorts) (bool, error)
|
||||
UnblockAllIPs() error
|
||||
@@ -53,15 +54,25 @@ func New(blockingRepository repository.BlockingRepository, logger log.Logger) AP
|
||||
}
|
||||
}
|
||||
|
||||
func (b *blocking) NftReload(blockListIP block.ListIP, blockListIPWithPort block.ListIPWithPort) error {
|
||||
func (b *blocking) NftReload(nft nftFirewall.NFT, blockListIP block.ListIP, blockListIPWithPort block.ListIPWithPort) error {
|
||||
b.mu.Lock()
|
||||
b.blockListIP = blockListIP
|
||||
b.blockListIPWithPort = blockListIPWithPort
|
||||
b.mu.Unlock()
|
||||
|
||||
batchBuilder, err := nft.NewBuildBatch()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err := batchBuilder.Close(); err != nil {
|
||||
b.logger.Warn(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
isExpiredEntries := false
|
||||
nowUnix := time.Now().Unix()
|
||||
err := b.blockingRepository.List(func(e entity.Blocking) error {
|
||||
err = b.blockingRepository.List(func(e entity.Blocking) error {
|
||||
ip := net.ParseIP(e.IP)
|
||||
if ip == nil {
|
||||
b.logger.Error(fmt.Sprintf("Failed to parse IP address: %s", e.IP))
|
||||
@@ -83,14 +94,14 @@ func (b *blocking) NftReload(blockListIP block.ListIP, blockListIPWithPort block
|
||||
b.logger.Error(fmt.Sprintf("Failed to parse ports: %s", err))
|
||||
return nil
|
||||
}
|
||||
if err := b.blockListIPWithPort.AddIP(ip, l4Ports, blockSeconds); err != nil {
|
||||
if err := b.blockListIPWithPort.AddBatchIP(batchBuilder, ip, l4Ports, blockSeconds); err != nil {
|
||||
b.logger.Error(fmt.Sprintf("Failed to add IP %s to block list: %s", ip.String(), err))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := b.blockListIP.AddIP(ip, blockSeconds); err != nil {
|
||||
if err := b.blockListIP.AddBatchIP(batchBuilder, ip, blockSeconds); err != nil {
|
||||
b.logger.Error(fmt.Sprintf("Failed to add IP %s to block list: %s", ip.String(), err))
|
||||
return nil
|
||||
}
|
||||
@@ -108,7 +119,11 @@ func (b *blocking) NftReload(blockListIP block.ListIP, blockListIPWithPort block
|
||||
}()
|
||||
}
|
||||
|
||||
return err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nft.RunBatch(batchBuilder)
|
||||
}
|
||||
|
||||
func (b *blocking) BlockIP(block BlockIP) (bool, error) {
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
type AfterLocalInput interface {
|
||||
AddRule(expr ...string) error
|
||||
AddRuleIn(AddRuleFunc func(expr ...string) error) error
|
||||
}
|
||||
|
||||
type afterLocalInput struct {
|
||||
nft nft.NFT
|
||||
family family.Type
|
||||
table string
|
||||
chain string
|
||||
}
|
||||
|
||||
func newAfterLocalInput(nft nft.NFT, family family.Type, table string) (LocalInput, error) {
|
||||
chain := "after-local-input"
|
||||
if err := nft.Chain().Add(family, table, chain, nftChain.TypeNone); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &afterLocalInput{
|
||||
nft: nft,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *afterLocalInput) AddRule(expr ...string) error {
|
||||
return l.nft.Rule().Add(l.family, l.table, l.chain, expr...)
|
||||
}
|
||||
|
||||
func (l *afterLocalInput) AddRuleIn(AddRuleFunc func(expr ...string) error) error {
|
||||
return AddRuleFunc("iifname != \"lo\" counter jump " + l.chain)
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
type BeforeLocalInput interface {
|
||||
AddRule(expr ...string) error
|
||||
AddRuleIn(AddRuleFunc func(expr ...string) error) error
|
||||
}
|
||||
|
||||
type beforeLocalInput struct {
|
||||
nft nft.NFT
|
||||
family family.Type
|
||||
table string
|
||||
chain string
|
||||
}
|
||||
|
||||
func newBeforeLocalInput(nft nft.NFT, family family.Type, table string) (LocalInput, error) {
|
||||
chain := "before-local-input"
|
||||
if err := nft.Chain().Add(family, table, chain, nftChain.TypeNone); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &beforeLocalInput{
|
||||
nft: nft,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *beforeLocalInput) AddRule(expr ...string) error {
|
||||
return l.nft.Rule().Add(l.family, l.table, l.chain, expr...)
|
||||
}
|
||||
|
||||
func (l *beforeLocalInput) AddRuleIn(AddRuleFunc func(expr ...string) error) error {
|
||||
return AddRuleFunc("iifname != \"lo\" counter jump " + l.chain)
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package block
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
type List interface {
|
||||
Name() string
|
||||
AddElement(element string) error
|
||||
DeleteElement(element string) error
|
||||
}
|
||||
|
||||
type list struct {
|
||||
nft nft.NFT
|
||||
family family.Type
|
||||
table string
|
||||
name string
|
||||
}
|
||||
|
||||
func newList(nft nft.NFT, family family.Type, table string, name string, params string) (List, error) {
|
||||
command := []string{
|
||||
"add set", family.String(), table, name, "{ " + params + " }",
|
||||
}
|
||||
if err := nft.Command().Run(command...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &list{
|
||||
nft: nft,
|
||||
family: family,
|
||||
table: table,
|
||||
name: name,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *list) Name() string {
|
||||
return l.name
|
||||
}
|
||||
|
||||
func (l *list) AddElement(element string) error {
|
||||
command := []string{
|
||||
"add element",
|
||||
l.family.String(), l.table, l.name,
|
||||
fmt.Sprintf("{ %s }", element),
|
||||
}
|
||||
return l.nft.Command().Run(command...)
|
||||
}
|
||||
|
||||
func (l *list) DeleteElement(element string) error {
|
||||
command := []string{
|
||||
"delete element",
|
||||
l.family.String(), l.table, l.name,
|
||||
fmt.Sprintf("{ %s }", element),
|
||||
}
|
||||
return l.nft.Command().Run(command...)
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
package block
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
type ListIP interface {
|
||||
// AddIP Add an IP address to the list.
|
||||
AddIP(addr net.IP, banSeconds uint32) error
|
||||
|
||||
// DeleteIP Delete an IP address from the list.
|
||||
DeleteIP(addr net.IP) error
|
||||
|
||||
// AddRuleToChain Add a rule to the parent chain.
|
||||
AddRuleToChain(chainAddRuleFunc func(expr ...string) error, action string) error
|
||||
}
|
||||
|
||||
type listIP struct {
|
||||
listIPv4 List
|
||||
listIPv6 List
|
||||
}
|
||||
|
||||
func NewListIP(nft nft.NFT, family family.Type, table string, name string) (ListIP, error) {
|
||||
params := "type ipv4_addr; flags interval, timeout;"
|
||||
listName := name + "_ip4"
|
||||
listIPv4, err := newList(nft, family, table, listName, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params = "type ipv6_addr; flags interval, timeout;"
|
||||
listName = name + "_ip6"
|
||||
listIPv6, err := newList(nft, family, table, listName, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &listIP{
|
||||
listIPv4: listIPv4,
|
||||
listIPv6: listIPv6,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *listIP) AddIP(addr net.IP, banSeconds uint32) error {
|
||||
el := []string{addr.String()}
|
||||
if banSeconds > 0 {
|
||||
el = append(el, "timeout", fmt.Sprintf("%ds", banSeconds))
|
||||
}
|
||||
|
||||
element := strings.Join(el, " ")
|
||||
|
||||
if addr.To4() != nil {
|
||||
return l.listIPv4.AddElement(element)
|
||||
}
|
||||
|
||||
return l.listIPv6.AddElement(element)
|
||||
}
|
||||
|
||||
func (l *listIP) DeleteIP(addr net.IP) error {
|
||||
if addr == nil {
|
||||
return fmt.Errorf("IP address cannot be nil")
|
||||
}
|
||||
if addr.To4() != nil {
|
||||
return l.listIPv4.DeleteElement(addr.String())
|
||||
}
|
||||
|
||||
return l.listIPv6.DeleteElement(addr.String())
|
||||
}
|
||||
|
||||
func (l *listIP) AddRuleToChain(chainAddRuleFunc func(expr ...string) error, action string) error {
|
||||
rule := "ip saddr @" + l.listIPv4.Name() + " " + action
|
||||
if err := chainAddRuleFunc(rule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rule = "ip6 saddr @" + l.listIPv6.Name() + " " + action
|
||||
if err := chainAddRuleFunc(rule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
type Chain interface {
|
||||
AddRule(expr ...string) error
|
||||
ListRules() ([]Rule, error)
|
||||
RemoveRuleByHandle(handle uint64) error
|
||||
Clear() error
|
||||
}
|
||||
|
||||
type chain struct {
|
||||
nft nft.NFT
|
||||
family family.Type
|
||||
table string
|
||||
chain string
|
||||
}
|
||||
|
||||
type NftOutput struct {
|
||||
Nftables []NftElement `json:"nftables"`
|
||||
}
|
||||
type NftElement struct {
|
||||
Rule *Rule `json:"rule,omitempty"`
|
||||
}
|
||||
|
||||
type Rule struct {
|
||||
Handle uint64 `json:"handle"`
|
||||
Comment string `json:"comment"`
|
||||
}
|
||||
|
||||
func (c *chain) AddRule(expr ...string) error {
|
||||
return c.nft.Rule().Add(c.family, c.table, c.chain, expr...)
|
||||
}
|
||||
|
||||
func (c *chain) ListRules() ([]Rule, error) {
|
||||
args := []string{"-a", "-j", "list", "chain", c.family.String(), c.table, c.chain}
|
||||
jsonData, err := c.nft.Command().RunWithOutput(args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var output NftOutput
|
||||
if err := json.Unmarshal([]byte(jsonData), &output); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var rules []Rule
|
||||
for _, el := range output.Nftables {
|
||||
if el.Rule != nil {
|
||||
rules = append(rules, *el.Rule)
|
||||
}
|
||||
}
|
||||
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
func (c *chain) RemoveRuleByHandle(handle uint64) error {
|
||||
return c.nft.Rule().Delete(c.family, c.table, c.chain, handle)
|
||||
}
|
||||
|
||||
func (c *chain) Clear() error {
|
||||
return c.nft.Chain().Clear(c.family, c.table, c.chain)
|
||||
}
|
||||
@@ -1,252 +0,0 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
nftFamily "git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/chain/block"
|
||||
)
|
||||
|
||||
type Chains interface {
|
||||
NewPacketFilter(enable bool) error
|
||||
PacketFilter() PacketFilter
|
||||
|
||||
NewInput(chain string, defaultAllow bool, priority int) error
|
||||
Input() Input
|
||||
|
||||
NewOutput(chain string, defaultAllow bool, priority int) error
|
||||
Output() Output
|
||||
|
||||
NewForward(chain string, defaultAllow bool, priority int) error
|
||||
Forward() Forward
|
||||
|
||||
NewBeforeLocalInput() error
|
||||
BeforeLocalInput() BeforeLocalInput
|
||||
|
||||
NewLocalInput() error
|
||||
LocalInput() LocalInput
|
||||
|
||||
NewAfterLocalInput() error
|
||||
AfterLocalInput() AfterLocalInput
|
||||
|
||||
NewLocalOutput() error
|
||||
LocalOutput() LocalOutput
|
||||
|
||||
NewLocalForward() error
|
||||
LocalForward() LocalForward
|
||||
|
||||
ClearRules() error
|
||||
|
||||
NewNoneChain(chain string) (Chain, error)
|
||||
NewChain(chain string, baseChain nftChain.ChainOptions) (Chain, error)
|
||||
NewBlockListIP(name string) (block.ListIP, error)
|
||||
NewBlockListIPWithPort(name string) (block.ListIPWithPort, error)
|
||||
}
|
||||
|
||||
type chains struct {
|
||||
input Input
|
||||
output Output
|
||||
forward Forward
|
||||
packetFilter PacketFilter
|
||||
|
||||
beforeLocalInput BeforeLocalInput
|
||||
localInput LocalInput
|
||||
afterLocalInput AfterLocalInput
|
||||
|
||||
localOutput LocalOutput
|
||||
localForward LocalForward
|
||||
|
||||
family nftFamily.Type
|
||||
table string
|
||||
nft nft.NFT
|
||||
}
|
||||
|
||||
func NewChains(nft nft.NFT, table string) (Chains, error) {
|
||||
family := nftFamily.INET
|
||||
|
||||
if err := clearRules(nft, family, table); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := nft.Table().Add(family, table); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &chains{
|
||||
nft: nft,
|
||||
table: table,
|
||||
family: family,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *chains) NewPacketFilter(enable bool) error {
|
||||
filter, err := newPacketFilter(c.nft, c.family, c.table, enable)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.packetFilter = filter
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *chains) PacketFilter() PacketFilter {
|
||||
return c.packetFilter
|
||||
}
|
||||
|
||||
func (c *chains) NewInput(chain string, defaultAllow bool, priority int) error {
|
||||
input, err := newInput(c.nft, c.family, c.table, chain, defaultAllow, priority)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.input = input
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *chains) Input() Input {
|
||||
return c.input
|
||||
}
|
||||
|
||||
func (c *chains) NewOutput(chain string, defaultAllow bool, priority int) error {
|
||||
output, err := newOutput(c.nft, c.family, c.table, chain, defaultAllow, priority)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.output = output
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *chains) Output() Output {
|
||||
return c.output
|
||||
}
|
||||
|
||||
func (c *chains) NewForward(chain string, defaultAllow bool, priority int) error {
|
||||
forward, err := newForward(c.nft, c.family, c.table, chain, defaultAllow, priority)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.forward = forward
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *chains) Forward() Forward {
|
||||
return c.forward
|
||||
}
|
||||
|
||||
func (c *chains) NewBeforeLocalInput() error {
|
||||
newChain, err := newBeforeLocalInput(c.nft, c.family, c.table)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.beforeLocalInput = newChain
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *chains) BeforeLocalInput() BeforeLocalInput {
|
||||
return c.beforeLocalInput
|
||||
}
|
||||
|
||||
func (c *chains) NewLocalInput() error {
|
||||
localInput, err := newLocalInput(c.nft, c.family, c.table)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.localInput = localInput
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *chains) LocalInput() LocalInput {
|
||||
return c.localInput
|
||||
}
|
||||
|
||||
func (c *chains) NewAfterLocalInput() error {
|
||||
newChain, err := newAfterLocalInput(c.nft, c.family, c.table)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.afterLocalInput = newChain
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *chains) AfterLocalInput() AfterLocalInput {
|
||||
return c.afterLocalInput
|
||||
}
|
||||
|
||||
func (c *chains) NewLocalOutput() error {
|
||||
localOutput, err := newLocalOutput(c.nft, c.family, c.table)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.localOutput = localOutput
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *chains) LocalOutput() LocalOutput {
|
||||
return c.localOutput
|
||||
}
|
||||
|
||||
func (c *chains) NewLocalForward() error {
|
||||
localForward, err := newLocalForward(c.nft, c.family, c.table)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.localForward = localForward
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *chains) LocalForward() LocalForward {
|
||||
return c.localForward
|
||||
}
|
||||
|
||||
func (c *chains) ClearRules() error {
|
||||
return clearRules(c.nft, c.family, c.table)
|
||||
}
|
||||
|
||||
func (c *chains) NewNoneChain(chainName string) (Chain, error) {
|
||||
return c.NewChain(chainName, nftChain.TypeNone)
|
||||
}
|
||||
|
||||
func (c *chains) NewChain(chainName string, baseChain nftChain.ChainOptions) (Chain, error) {
|
||||
if err := c.nft.Chain().Add(c.family, c.table, chainName, baseChain); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &chain{
|
||||
nft: c.nft,
|
||||
family: c.family,
|
||||
table: c.table,
|
||||
chain: chainName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *chains) NewBlockListIP(name string) (block.ListIP, error) {
|
||||
blockList, err := block.NewListIP(c.nft, c.family, c.table, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return blockList, nil
|
||||
}
|
||||
|
||||
func (c *chains) NewBlockListIPWithPort(name string) (block.ListIPWithPort, error) {
|
||||
blockList, err := block.NewListIPWithPort(c.nft, c.family, c.table, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return blockList, nil
|
||||
}
|
||||
|
||||
func clearRules(nft nft.NFT, family nftFamily.Type, table string) error {
|
||||
if err := nft.Table().Delete(family, table); err != nil {
|
||||
if !strings.Contains(string(err.Error()), "delete table "+family.String()+" "+table) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
type Forward interface {
|
||||
AddRule(expr ...string) error
|
||||
}
|
||||
|
||||
type forward struct {
|
||||
nft nft.NFT
|
||||
family family.Type
|
||||
table string
|
||||
chain string
|
||||
}
|
||||
|
||||
func newForward(nft nft.NFT, family family.Type, table string, chain string, defaultAllow bool, priority int) (Forward, error) {
|
||||
policy := nftChain.PolicyDrop
|
||||
if defaultAllow {
|
||||
policy = nftChain.PolicyAccept
|
||||
}
|
||||
|
||||
baseChain := nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeFilter,
|
||||
Hook: nftChain.HookForward,
|
||||
Priority: int32(priority),
|
||||
Policy: policy,
|
||||
Device: "",
|
||||
}
|
||||
|
||||
if err := nft.Chain().Add(family, table, chain, baseChain); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &forward{
|
||||
nft: nft,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *forward) AddRule(expr ...string) error {
|
||||
return c.nft.Rule().Add(c.family, c.table, c.chain, expr...)
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
type Input interface {
|
||||
AddRule(expr ...string) error
|
||||
}
|
||||
|
||||
type input struct {
|
||||
nft nft.NFT
|
||||
family family.Type
|
||||
table string
|
||||
chain string
|
||||
}
|
||||
|
||||
func newInput(nft nft.NFT, family family.Type, table string, chain string, defaultAllow bool, priority int) (Input, error) {
|
||||
policy := nftChain.PolicyDrop
|
||||
if defaultAllow {
|
||||
policy = nftChain.PolicyAccept
|
||||
}
|
||||
|
||||
baseChain := nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeFilter,
|
||||
Hook: nftChain.HookInput,
|
||||
Priority: int32(priority),
|
||||
Policy: policy,
|
||||
Device: "",
|
||||
}
|
||||
|
||||
if err := nft.Chain().Add(family, table, chain, baseChain); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &input{
|
||||
nft: nft,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *input) AddRule(expr ...string) error {
|
||||
return c.nft.Rule().Add(c.family, c.table, c.chain, expr...)
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
type LocalForward interface {
|
||||
AddRule(expr ...string) error
|
||||
AddRuleIn(AddRuleFunc func(expr ...string) error) error
|
||||
}
|
||||
|
||||
type localForward struct {
|
||||
nft nft.NFT
|
||||
family family.Type
|
||||
table string
|
||||
chain string
|
||||
}
|
||||
|
||||
func newLocalForward(nft nft.NFT, family family.Type, table string) (LocalForward, error) {
|
||||
chain := "local-forward"
|
||||
if err := nft.Chain().Add(family, table, chain, nftChain.TypeNone); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &localForward{
|
||||
nft: nft,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *localForward) AddRule(expr ...string) error {
|
||||
return l.nft.Rule().Add(l.family, l.table, l.chain, expr...)
|
||||
}
|
||||
|
||||
func (l *localForward) AddRuleIn(AddRuleFunc func(expr ...string) error) error {
|
||||
return AddRuleFunc("iifname != \"lo\" counter jump " + l.chain)
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
type LocalInput interface {
|
||||
AddRule(expr ...string) error
|
||||
AddRuleIn(AddRuleFunc func(expr ...string) error) error
|
||||
}
|
||||
|
||||
type localInput struct {
|
||||
nft nft.NFT
|
||||
family family.Type
|
||||
table string
|
||||
chain string
|
||||
}
|
||||
|
||||
func newLocalInput(nft nft.NFT, family family.Type, table string) (LocalInput, error) {
|
||||
chain := "local-input"
|
||||
if err := nft.Chain().Add(family, table, chain, nftChain.TypeNone); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &localInput{
|
||||
nft: nft,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *localInput) AddRule(expr ...string) error {
|
||||
return l.nft.Rule().Add(l.family, l.table, l.chain, expr...)
|
||||
}
|
||||
|
||||
func (l *localInput) AddRuleIn(AddRuleFunc func(expr ...string) error) error {
|
||||
return AddRuleFunc("iifname != \"lo\" counter jump " + l.chain)
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
type LocalOutput interface {
|
||||
AddRule(expr ...string) error
|
||||
AddRuleOut(AddRuleFunc func(expr ...string) error) error
|
||||
}
|
||||
|
||||
type localOutput struct {
|
||||
nft nft.NFT
|
||||
family family.Type
|
||||
table string
|
||||
chain string
|
||||
}
|
||||
|
||||
func newLocalOutput(nft nft.NFT, family family.Type, table string) (LocalOutput, error) {
|
||||
chain := "local-output"
|
||||
if err := nft.Chain().Add(family, table, chain, nftChain.TypeNone); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &localOutput{
|
||||
nft: nft,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *localOutput) AddRule(expr ...string) error {
|
||||
return l.nft.Rule().Add(l.family, l.table, l.chain, expr...)
|
||||
}
|
||||
|
||||
func (l *localOutput) AddRuleOut(AddRuleFunc func(expr ...string) error) error {
|
||||
return AddRuleFunc("oifname != \"lo\" counter jump " + l.chain)
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
type Output interface {
|
||||
AddRule(expr ...string) error
|
||||
}
|
||||
|
||||
type output struct {
|
||||
nft nft.NFT
|
||||
family family.Type
|
||||
table string
|
||||
chain string
|
||||
}
|
||||
|
||||
func newOutput(nft nft.NFT, family family.Type, table string, chain string, defaultAllow bool, priority int) (Output, error) {
|
||||
policy := nftChain.PolicyDrop
|
||||
if defaultAllow {
|
||||
policy = nftChain.PolicyAccept
|
||||
}
|
||||
|
||||
baseChain := nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeFilter,
|
||||
Hook: nftChain.HookOutput,
|
||||
Priority: int32(priority),
|
||||
Policy: policy,
|
||||
Device: "",
|
||||
}
|
||||
|
||||
if err := nft.Chain().Add(family, table, chain, baseChain); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &output{
|
||||
nft: nft,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *output) AddRule(expr ...string) error {
|
||||
return c.nft.Rule().Add(c.family, c.table, c.chain, expr...)
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
nftFamily "git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
type PacketFilter interface {
|
||||
AddRuleIn(AddRuleFunc func(expr ...string) error) error
|
||||
AddRuleOut(AddRuleFunc func(expr ...string) error) error
|
||||
}
|
||||
|
||||
type packetFilter struct {
|
||||
enable bool
|
||||
invalidName string
|
||||
}
|
||||
|
||||
// newPacketFilter Drop out of order packets and packets in an INVALID state in nftables connection tracking.
|
||||
func newPacketFilter(nft nft.NFT, family nftFamily.Type, table string, enable bool) (PacketFilter, error) {
|
||||
chainInvalidName := "INVALID"
|
||||
if !enable {
|
||||
return &packetFilter{
|
||||
enable: enable,
|
||||
invalidName: chainInvalidName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
chainName := "INVDROP"
|
||||
|
||||
if err := nft.Chain().Add(family, table, chainName, nftChain.TypeNone); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := nft.Rule().Add(family, table, chainName, "counter drop"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := nft.Chain().Add(family, table, chainInvalidName, nftChain.TypeNone); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := nft.Rule().Add(family, table, chainInvalidName, "ct state invalid counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := nft.Rule().Add(family, table, chainInvalidName, "tcp flags ! fin,syn,rst,psh,ack,urg counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := nft.Rule().Add(family, table, chainInvalidName, "tcp flags & (fin | syn | rst | psh | ack | urg) == fin | syn | rst | psh | ack | urg counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := nft.Rule().Add(family, table, chainInvalidName, "tcp flags & (fin | syn) == fin | syn counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := nft.Rule().Add(family, table, chainInvalidName, "tcp flags & (syn | rst) == syn | rst counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := nft.Rule().Add(family, table, chainInvalidName, "tcp flags & (fin | rst) == fin | rst counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := nft.Rule().Add(family, table, chainInvalidName, "tcp flags & (fin | ack) == fin counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := nft.Rule().Add(family, table, chainInvalidName, "tcp flags & (psh | ack) == psh counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := nft.Rule().Add(family, table, chainInvalidName, "tcp flags & (ack | urg) == urg counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := nft.Rule().Add(family, table, chainInvalidName, "tcp flags & (fin | syn | rst | ack) != syn ct state new counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &packetFilter{
|
||||
enable: enable,
|
||||
invalidName: chainInvalidName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *packetFilter) AddRuleIn(AddRuleFunc func(expr ...string) error) error {
|
||||
if !f.enable {
|
||||
return nil
|
||||
}
|
||||
return AddRuleFunc("iifname != \"lo\" meta l4proto tcp counter jump " + f.invalidName)
|
||||
}
|
||||
|
||||
func (f *packetFilter) AddRuleOut(AddRuleFunc func(expr ...string) error) error {
|
||||
if !f.enable {
|
||||
return nil
|
||||
}
|
||||
return AddRuleFunc("oifname != \"lo\" meta l4proto tcp counter jump " + f.invalidName)
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
package firewall
|
||||
package config
|
||||
|
||||
import (
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/types"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/pkg/ip"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
@@ -12,9 +13,11 @@ type Config struct {
|
||||
Options ConfigOptions
|
||||
MetadataNaming ConfigMetadata
|
||||
Policy ConfigPolicy
|
||||
PortKnocking []ConfigPortKnocking
|
||||
}
|
||||
|
||||
type ConfigOptions struct {
|
||||
Cache bool
|
||||
ClearMode ClearMode
|
||||
SavesRules bool
|
||||
SavesRulesPath string
|
||||
@@ -74,6 +77,19 @@ type ConfigIP struct {
|
||||
LimitRate string
|
||||
}
|
||||
|
||||
type ConfigPortKnocking struct {
|
||||
Name string
|
||||
Port types.L4Port
|
||||
IPVersion ip.Version
|
||||
Knocks []*ConfigKnock
|
||||
}
|
||||
|
||||
type ConfigKnock struct {
|
||||
Port types.L4Port
|
||||
Action types.KnockAction
|
||||
Timeout uint32
|
||||
}
|
||||
|
||||
type ClearMode int8
|
||||
|
||||
const (
|
||||
@@ -1,72 +0,0 @@
|
||||
package firewall
|
||||
|
||||
import nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
|
||||
func (f *firewall) reloadDocker() error {
|
||||
f.logger.Debug("Reload docker rules")
|
||||
if err := f.reloadDockerPrerouting(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *firewall) reloadDockerPrerouting() error {
|
||||
preroutingNat, err := f.chains.NewChain("prerouting_nat", nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeNat,
|
||||
Hook: nftChain.HookPrerouting,
|
||||
Priority: -100,
|
||||
Policy: nftChain.PolicyAccept,
|
||||
Device: "",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.docker.NftChains().PreroutingNatJump(preroutingNat.AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
preroutingFilter, err := f.chains.NewChain("prerouting_filter", nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeFilter,
|
||||
Hook: nftChain.HookPrerouting,
|
||||
Priority: -300,
|
||||
Policy: nftChain.PolicyAccept,
|
||||
Device: "",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.docker.NftChains().PreroutingFilterJump(preroutingFilter.AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
outputNat, err := f.chains.NewChain("output_nat", nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeNat,
|
||||
Hook: nftChain.HookOutput,
|
||||
Priority: -100,
|
||||
Policy: nftChain.PolicyAccept,
|
||||
Device: "",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.docker.NftChains().OutputNatJump(outputNat.AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
postroutingNat, err := f.chains.NewChain("postrouting_nat", nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeNat,
|
||||
Hook: nftChain.HookPostrouting,
|
||||
Priority: 300,
|
||||
Policy: nftChain.PolicyAccept,
|
||||
Device: "",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.docker.NftChains().PostroutingNatJump(postroutingNat.AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -4,18 +4,26 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/blocking"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
nftables "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/blocklist"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor"
|
||||
dockerFirewall "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/firewall"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/blocking"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/config"
|
||||
nftFirewall "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/table"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/reload"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/info"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/pkg/filesystem"
|
||||
)
|
||||
|
||||
type API interface {
|
||||
// Reload Clear all rules and set new rules.
|
||||
Reload() error
|
||||
Reload(daemonInfo info.Info) error
|
||||
|
||||
// SavesRules Save rules to file.
|
||||
SavesRules()
|
||||
@@ -43,69 +51,96 @@ type API interface {
|
||||
}
|
||||
|
||||
type firewall struct {
|
||||
nft nftables.NFT
|
||||
nft nftFirewall.NFT
|
||||
table table.Table
|
||||
logger log.Logger
|
||||
config *Config
|
||||
config *config.Config
|
||||
blockingService blocking.API
|
||||
chains chain.Chains
|
||||
docker docker_monitor.Docker
|
||||
blocklist blocklist.Blocklist
|
||||
dataDir string
|
||||
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func New(pathNFT string, blockingService blocking.API, logger log.Logger, config Config, docker docker_monitor.Docker) (API, error) {
|
||||
nft, err := nftables.NewWithPath(pathNFT)
|
||||
func New(
|
||||
pathNFT string,
|
||||
blockingService blocking.API,
|
||||
logger log.Logger,
|
||||
config config.Config,
|
||||
docker docker_monitor.Docker,
|
||||
blocklist blocklist.Blocklist,
|
||||
dataDir string,
|
||||
) (API, error) {
|
||||
nftClient, err := nftables.NewWithPath(pathNFT)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create nft client: %w %s", err, pathNFT)
|
||||
}
|
||||
|
||||
return &firewall{
|
||||
nft: nft,
|
||||
nft: nftFirewall.New(nftClient, strings.TrimRight(dataDir, "/")+"/tmp"),
|
||||
logger: logger,
|
||||
config: &config,
|
||||
blockingService: blockingService,
|
||||
docker: docker,
|
||||
blocklist: blocklist,
|
||||
dataDir: dataDir,
|
||||
|
||||
mu: sync.Mutex{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *firewall) Reload() error {
|
||||
func (f *firewall) Reload(daemonInfo info.Info) error {
|
||||
f.logger.Debug("Reload nftables rules")
|
||||
if f.config.Options.ClearMode == ClearModeGlobal {
|
||||
if err := f.nft.Clear(); err != nil {
|
||||
|
||||
nftReload := reload.New(f.nft, f.logger, f.config)
|
||||
blocklistNames := f.blocklist.Names()
|
||||
|
||||
var nftTable table.Table
|
||||
var err error
|
||||
if f.config.Options.Cache {
|
||||
file := f.pathFileCacheNFT()
|
||||
nftTable, err = nftReload.RunWithCache(
|
||||
file,
|
||||
f.isValidCacheFile(daemonInfo),
|
||||
blocklistNames,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
checksum, err := filesystem.FileChecksum(file)
|
||||
if err != nil {
|
||||
f.logger.Error(fmt.Sprintf("Failed to calculate checksum for %s: %s", file, err))
|
||||
} else if err := daemonInfo.Metadata().FirewallFileNft().Update(checksum); err != nil {
|
||||
f.logger.Error(fmt.Sprintf("Failed to update metadata: %s", err))
|
||||
}
|
||||
} else {
|
||||
nftTable, err = nftReload.Run(blocklistNames)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
chains, err := chain.NewChains(f.nft, f.config.MetadataNaming.TableName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.chains = chains
|
||||
f.mu.Lock()
|
||||
f.table = nftTable
|
||||
f.mu.Unlock()
|
||||
|
||||
if err := f.docker.NftReload(f.chains.NewNoneChain); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := f.chains.NewPacketFilter(f.config.Options.PacketFilter); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.reloadInput(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.reloadOutput(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.reloadForward(); err != nil {
|
||||
return err
|
||||
}
|
||||
if f.config.Options.DockerSupport {
|
||||
if err := f.reloadDocker(); err != nil {
|
||||
if f.config.Options.DockerSupport && nftTable.DockerChains() != nil {
|
||||
nftDocker := dockerFirewall.NewNFT(f.nft, nftTable.DockerChains())
|
||||
if err := f.docker.NftReload(nftDocker); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := f.reloadBlockList(); err != nil {
|
||||
if err := f.blockingService.NftReload(f.nft, nftTable.BlockList().ListIP(), nftTable.BlockList().ListIPWithPort()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := f.blocklist.NftReload(nftTable.BlockList().Blocks()); err != nil {
|
||||
f.logger.Error(fmt.Sprintf("Failed to reload blocklist: %s", err))
|
||||
}
|
||||
|
||||
f.logger.Debug("Reload nftables rules done")
|
||||
return nil
|
||||
}
|
||||
@@ -114,13 +149,17 @@ func (f *firewall) ClearRules() {
|
||||
f.logger.Debug("Clear nftables rules")
|
||||
|
||||
switch f.config.Options.ClearMode {
|
||||
case ClearModeGlobal:
|
||||
if err := f.nft.Clear(); err != nil {
|
||||
case config.ClearModeGlobal:
|
||||
if err := f.nft.NFT().Clear(); err != nil {
|
||||
f.logger.Error(fmt.Sprintf("Failed to clear rules: %s", err))
|
||||
}
|
||||
break
|
||||
case ClearModeOwn:
|
||||
if err := f.chains.ClearRules(); err != nil {
|
||||
case config.ClearModeOwn:
|
||||
if f.table == nil {
|
||||
f.logger.Error("table is nil")
|
||||
return
|
||||
}
|
||||
if err := f.table.Clear(); err != nil {
|
||||
f.logger.Error(fmt.Sprintf("Failed to clear rules: %s", err))
|
||||
}
|
||||
break
|
||||
@@ -153,7 +192,7 @@ func (f *firewall) SavesRules() {
|
||||
}
|
||||
|
||||
args := []string{"list", "ruleset"}
|
||||
output, err := f.nft.Command().RunWithOutput(args...)
|
||||
output, err := f.nft.NFT().Command().RunWithOutput(args...)
|
||||
if err != nil {
|
||||
f.logger.Warn(fmt.Sprintf("Failed to save rules: %s", err))
|
||||
return
|
||||
@@ -190,3 +229,47 @@ func (f *firewall) BlockIPWithPorts(blockIP blocking.BlockIPWithPorts) (bool, er
|
||||
func (f *firewall) DockerSupport() bool {
|
||||
return f.config.Options.DockerSupport
|
||||
}
|
||||
|
||||
func (f *firewall) pathFileCacheNFT() string {
|
||||
return strings.TrimRight(f.dataDir, "/") + "/nftables.nft"
|
||||
}
|
||||
|
||||
func (f *firewall) isValidCacheFile(daemonInfo info.Info) bool {
|
||||
if daemonInfo.IsVersionChanged() {
|
||||
f.logger.Debug("Version changed, skip cache")
|
||||
return false
|
||||
}
|
||||
|
||||
if daemonInfo.IsSettingsChanged() {
|
||||
f.logger.Debug("Settings changed, skip cache")
|
||||
return false
|
||||
}
|
||||
|
||||
fileNFT := f.pathFileCacheNFT()
|
||||
if !filesystem.FileExists(fileNFT) {
|
||||
return false
|
||||
}
|
||||
|
||||
metadataChecksum, err := daemonInfo.Metadata().FirewallFileNft().Get()
|
||||
if err != nil {
|
||||
f.logger.Error(fmt.Sprintf("Failed to get checksum: %s", err))
|
||||
return false
|
||||
}
|
||||
|
||||
if metadataChecksum == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
checksum, err := filesystem.FileChecksum(fileNFT)
|
||||
if err != nil {
|
||||
f.logger.Error(fmt.Sprintf("Failed to calculate checksum for %s: %s", fileNFT, err))
|
||||
return false
|
||||
}
|
||||
|
||||
if checksum != metadataChecksum {
|
||||
f.logger.Warn(fmt.Sprintf("Checksum of %s is not equal to metadata checksum", fileNFT))
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package block
|
||||
|
||||
import (
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
type Sets interface {
|
||||
Add(name string, params string) error
|
||||
}
|
||||
|
||||
type setBatch struct {
|
||||
builder nft.BatchBuilder
|
||||
family family.Type
|
||||
table string
|
||||
}
|
||||
|
||||
func NewBatchSet(builder nft.BatchBuilder, family family.Type, table string) Sets {
|
||||
return &setBatch{
|
||||
builder: builder,
|
||||
family: family,
|
||||
table: table,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *setBatch) Add(name string, params string) error {
|
||||
command := []string{
|
||||
"add set", b.family.String(), b.table, name, "{ " + params + " }",
|
||||
}
|
||||
return b.builder.Command().Run(command...)
|
||||
}
|
||||
|
||||
func getNamesIP(name string) (ipV4 string, ipV6 string) {
|
||||
return name + "_ip4", name + "_ip6"
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package block
|
||||
|
||||
import (
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
nftFirewall "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/rule"
|
||||
)
|
||||
|
||||
type Blocklist interface {
|
||||
// ReplaceElements Replace the elements of the list.
|
||||
ReplaceElements(ipV4 []string, ipV6 []string, pathSaveNft string) error
|
||||
|
||||
ReplaceElementsWithFile(pathNft string) error
|
||||
|
||||
// AddRuleToChain Add a rule to the parent chain.
|
||||
AddRuleToChain(chainAddRuleFunc rule.AddFunc, action string) error
|
||||
}
|
||||
|
||||
type blocklist struct {
|
||||
nft nftFirewall.NFT
|
||||
|
||||
listIPv4 List
|
||||
listIPv6 List
|
||||
}
|
||||
|
||||
func NewBlocklist(nft nftFirewall.NFT, builder nft.BatchBuilder, family family.Type, table string, name string) (Blocklist, error) {
|
||||
listNameV4, listNameV6 := getNamesIP(name)
|
||||
|
||||
params := "type ipv4_addr; flags interval; auto-merge;"
|
||||
listIPv4, err := newList(nft, builder, family, table, listNameV4, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params = "type ipv6_addr; flags interval; auto-merge;"
|
||||
listIPv6, err := newList(nft, builder, family, table, listNameV6, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &blocklist{
|
||||
nft: nft,
|
||||
|
||||
listIPv4: listIPv4,
|
||||
listIPv6: listIPv6,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewBlocklistWithoutCommand(nft nftFirewall.NFT, family family.Type, table string, name string) Blocklist {
|
||||
listNameV4, listNameV6 := getNamesIP(name)
|
||||
|
||||
listIPv4 := newListWithoutCommand(nft, family, table, listNameV4)
|
||||
listIPv6 := newListWithoutCommand(nft, family, table, listNameV6)
|
||||
|
||||
return &blocklist{
|
||||
nft: nft,
|
||||
|
||||
listIPv4: listIPv4,
|
||||
listIPv6: listIPv6,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *blocklist) ReplaceElements(ipV4 []string, ipV6 []string, pathSaveNft string) error {
|
||||
batchBuilder, err := l.nft.NewBuildBatch()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = batchBuilder.Close()
|
||||
}()
|
||||
|
||||
if err := l.listIPv4.ReplaceBatchElements(batchBuilder, ipV4); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := l.listIPv6.ReplaceBatchElements(batchBuilder, ipV6); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return l.nft.RunBatchAndMoveFile(batchBuilder, pathSaveNft)
|
||||
}
|
||||
|
||||
func (l *blocklist) ReplaceElementsWithFile(pathNft string) error {
|
||||
args := []string{"-f", pathNft}
|
||||
return l.nft.NFT().Command().Run(args...)
|
||||
}
|
||||
|
||||
func (l *blocklist) AddRuleToChain(chainAddRuleFunc rule.AddFunc, action string) error {
|
||||
addRule := "ip saddr @" + l.listIPv4.Name() + " " + action
|
||||
if err := chainAddRuleFunc(addRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
addRule = "ip6 saddr @" + l.listIPv6.Name() + " " + action
|
||||
if err := chainAddRuleFunc(addRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package block
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
nftFirewall "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft"
|
||||
)
|
||||
|
||||
type List interface {
|
||||
Name() string
|
||||
AddElement(element string) error
|
||||
AddBatchElement(builder nft.BatchBuilder, element string) error
|
||||
DeleteElement(element string) error
|
||||
ReplaceElements(elements []string) error
|
||||
ReplaceBatchElements(builder nft.BatchBuilder, elements []string) error
|
||||
}
|
||||
|
||||
type list struct {
|
||||
nft nftFirewall.NFT
|
||||
family family.Type
|
||||
table string
|
||||
name string
|
||||
}
|
||||
|
||||
func newList(nft nftFirewall.NFT, builder nft.BatchBuilder, family family.Type, table string, name string, params string) (List, error) {
|
||||
command := []string{
|
||||
"add set", family.String(), table, name, "{ " + params + " }",
|
||||
}
|
||||
if err := builder.Command().Run(command...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &list{
|
||||
nft: nft,
|
||||
family: family,
|
||||
table: table,
|
||||
name: name,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func newListWithoutCommand(nft nftFirewall.NFT, family family.Type, table string, name string) List {
|
||||
return &list{
|
||||
nft: nft,
|
||||
family: family,
|
||||
table: table,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *list) Name() string {
|
||||
return l.name
|
||||
}
|
||||
|
||||
func (l *list) AddElement(element string) error {
|
||||
command := []string{
|
||||
"add element",
|
||||
l.family.String(), l.table, l.name,
|
||||
fmt.Sprintf("{ %s }", element),
|
||||
}
|
||||
return l.nft.NFT().Command().Run(command...)
|
||||
}
|
||||
|
||||
func (l *list) AddBatchElement(builder nft.BatchBuilder, element string) error {
|
||||
command := []string{
|
||||
"add element",
|
||||
l.family.String(), l.table, l.name,
|
||||
fmt.Sprintf("{ %s }", element),
|
||||
}
|
||||
return builder.Command().Run(command...)
|
||||
}
|
||||
|
||||
func (l *list) DeleteElement(element string) error {
|
||||
command := []string{
|
||||
"delete element",
|
||||
l.family.String(), l.table, l.name,
|
||||
fmt.Sprintf("{ %s }", element),
|
||||
}
|
||||
return l.nft.NFT().Command().Run(command...)
|
||||
}
|
||||
|
||||
func (l *list) ReplaceElements(elements []string) error {
|
||||
batchBuilder, err := l.nft.NewBuildBatch()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = batchBuilder.Close()
|
||||
}()
|
||||
|
||||
if err := l.replaceElements(batchBuilder, elements); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return l.nft.RunBatch(batchBuilder)
|
||||
}
|
||||
|
||||
func (l *list) ReplaceBatchElements(builder nft.BatchBuilder, elements []string) error {
|
||||
return l.replaceElements(builder, elements)
|
||||
}
|
||||
|
||||
func (l *list) replaceElements(builder nft.BatchBuilder, elements []string) error {
|
||||
if err := builder.Command().Run("flush set", l.family.String(), l.table, l.name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(elements) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
command := []string{
|
||||
"add element",
|
||||
l.family.String(), l.table, l.name,
|
||||
fmt.Sprintf("{ %s }", strings.Join(elements, ",")),
|
||||
}
|
||||
|
||||
return builder.Command().Run(command...)
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package block
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
nftFirewall "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/rule"
|
||||
)
|
||||
|
||||
type ListIP interface {
|
||||
// AddIP Add an IP address to the list.
|
||||
AddIP(addr net.IP, banSeconds uint32) error
|
||||
|
||||
// AddBatchIP Add an IP address to the list.
|
||||
AddBatchIP(builder nft.BatchBuilder, addr net.IP, banSeconds uint32) error
|
||||
|
||||
// DeleteIP Delete an IP address from the list.
|
||||
DeleteIP(addr net.IP) error
|
||||
|
||||
// AddRuleToChain Add a rule to the parent chain.
|
||||
AddRuleToChain(chainAddRuleFunc rule.AddFunc, action string) error
|
||||
}
|
||||
|
||||
type listIP struct {
|
||||
listIPv4 List
|
||||
listIPv6 List
|
||||
}
|
||||
|
||||
func NewListIP(nft nftFirewall.NFT, builder nft.BatchBuilder, family family.Type, table string, name string) (ListIP, error) {
|
||||
listNameV4, listNameV6 := getNamesIP(name)
|
||||
|
||||
params := "type ipv4_addr; flags interval, timeout;"
|
||||
listIPv4, err := newList(nft, builder, family, table, listNameV4, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params = "type ipv6_addr; flags interval, timeout;"
|
||||
listIPv6, err := newList(nft, builder, family, table, listNameV6, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &listIP{
|
||||
listIPv4: listIPv4,
|
||||
listIPv6: listIPv6,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewListIPWithoutCommand(nft nftFirewall.NFT, family family.Type, table string, name string) ListIP {
|
||||
listNameV4, listNameV6 := getNamesIP(name)
|
||||
|
||||
listIPv4 := newListWithoutCommand(nft, family, table, listNameV4)
|
||||
listIPv6 := newListWithoutCommand(nft, family, table, listNameV6)
|
||||
|
||||
return &listIP{
|
||||
listIPv4: listIPv4,
|
||||
listIPv6: listIPv6,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *listIP) AddIP(addr net.IP, banSeconds uint32) error {
|
||||
el := []string{addr.String()}
|
||||
if banSeconds > 0 {
|
||||
el = append(el, "timeout", fmt.Sprintf("%ds", banSeconds))
|
||||
}
|
||||
|
||||
element := strings.Join(el, " ")
|
||||
|
||||
if addr.To4() != nil {
|
||||
return l.listIPv4.AddElement(element)
|
||||
}
|
||||
|
||||
return l.listIPv6.AddElement(element)
|
||||
}
|
||||
|
||||
func (l *listIP) AddBatchIP(builder nft.BatchBuilder, addr net.IP, banSeconds uint32) error {
|
||||
el := []string{addr.String()}
|
||||
if banSeconds > 0 {
|
||||
el = append(el, "timeout", fmt.Sprintf("%ds", banSeconds))
|
||||
}
|
||||
|
||||
element := strings.Join(el, " ")
|
||||
|
||||
if addr.To4() != nil {
|
||||
return l.listIPv4.AddBatchElement(builder, element)
|
||||
}
|
||||
|
||||
return l.listIPv6.AddBatchElement(builder, element)
|
||||
}
|
||||
|
||||
func (l *listIP) DeleteIP(addr net.IP) error {
|
||||
if addr == nil {
|
||||
return fmt.Errorf("IP address cannot be nil")
|
||||
}
|
||||
if addr.To4() != nil {
|
||||
return l.listIPv4.DeleteElement(addr.String())
|
||||
}
|
||||
|
||||
return l.listIPv6.DeleteElement(addr.String())
|
||||
}
|
||||
|
||||
func (l *listIP) AddRuleToChain(chainAddRuleFunc rule.AddFunc, action string) error {
|
||||
rule := "ip saddr @" + l.listIPv4.Name() + " " + action
|
||||
if err := chainAddRuleFunc(rule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rule = "ip6 saddr @" + l.listIPv6.Name() + " " + action
|
||||
if err := chainAddRuleFunc(rule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+48
-8
@@ -5,8 +5,10 @@ import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
nftFirewall "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/rule"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/types"
|
||||
)
|
||||
|
||||
@@ -14,11 +16,14 @@ type ListIPWithPort interface {
|
||||
// AddIP Add an IP address to the list.
|
||||
AddIP(addr net.IP, ports []types.L4Port, banSeconds uint32) error
|
||||
|
||||
// AddBatchIP Add an IP address to the list.
|
||||
AddBatchIP(builder nft.BatchBuilder, addr net.IP, ports []types.L4Port, banSeconds uint32) error
|
||||
|
||||
// DeleteIP Delete an IP address from the list.
|
||||
DeleteIP(addr net.IP, port types.L4Port) error
|
||||
|
||||
// AddRuleToChain Add a rule to the parent chain.
|
||||
AddRuleToChain(chainAddRuleFunc func(expr ...string) error, action string) error
|
||||
AddRuleToChain(chainAddRuleFunc rule.AddFunc, action string) error
|
||||
}
|
||||
|
||||
type listIPWithPort struct {
|
||||
@@ -26,17 +31,17 @@ type listIPWithPort struct {
|
||||
listIPv6 List
|
||||
}
|
||||
|
||||
func NewListIPWithPort(nft nft.NFT, family family.Type, table string, name string) (ListIPWithPort, error) {
|
||||
func NewListIPWithPort(nft nftFirewall.NFT, builder nft.BatchBuilder, family family.Type, table string, name string) (ListIPWithPort, error) {
|
||||
listNameV4, listNameV6 := getNamesIP(name)
|
||||
|
||||
params := "type ipv4_addr . inet_proto . inet_service; flags interval, timeout;"
|
||||
listName := name + "_ip4"
|
||||
listIPv4, err := newList(nft, family, table, listName, params)
|
||||
listIPv4, err := newList(nft, builder, family, table, listNameV4, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params = "type ipv6_addr . inet_proto . inet_service; flags interval, timeout;"
|
||||
listName = name + "_ip6"
|
||||
listIPv6, err := newList(nft, family, table, listName, params)
|
||||
listIPv6, err := newList(nft, builder, family, table, listNameV6, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -47,6 +52,18 @@ func NewListIPWithPort(nft nft.NFT, family family.Type, table string, name strin
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewListIPWithPortWithoutCommand(nft nftFirewall.NFT, family family.Type, table string, name string) ListIPWithPort {
|
||||
listNameV4, listNameV6 := getNamesIP(name)
|
||||
|
||||
listIPv4 := newListWithoutCommand(nft, family, table, listNameV4)
|
||||
listIPv6 := newListWithoutCommand(nft, family, table, listNameV6)
|
||||
|
||||
return &listIPWithPort{
|
||||
listIPv4: listIPv4,
|
||||
listIPv6: listIPv6,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *listIPWithPort) AddIP(addr net.IP, ports []types.L4Port, banSeconds uint32) error {
|
||||
if len(ports) == 0 {
|
||||
return fmt.Errorf("ports is empty")
|
||||
@@ -70,6 +87,29 @@ func (l *listIPWithPort) AddIP(addr net.IP, ports []types.L4Port, banSeconds uin
|
||||
return l.listIPv6.AddElement(element)
|
||||
}
|
||||
|
||||
func (l *listIPWithPort) AddBatchIP(builder nft.BatchBuilder, addr net.IP, ports []types.L4Port, banSeconds uint32) error {
|
||||
if len(ports) == 0 {
|
||||
return fmt.Errorf("ports is empty")
|
||||
}
|
||||
|
||||
var elements []string
|
||||
for _, port := range ports {
|
||||
el := []string{fmt.Sprintf("%s . %s . %d", addr.String(), port.ProtocolString(), port.Number())}
|
||||
if banSeconds > 0 {
|
||||
el = append(el, "timeout", fmt.Sprintf("%ds", banSeconds))
|
||||
}
|
||||
|
||||
elements = append(elements, strings.Join(el, " "))
|
||||
}
|
||||
|
||||
element := strings.Join(elements, ",")
|
||||
if addr.To4() != nil {
|
||||
return l.listIPv4.AddBatchElement(builder, element)
|
||||
}
|
||||
|
||||
return l.listIPv6.AddBatchElement(builder, element)
|
||||
}
|
||||
|
||||
func (l *listIPWithPort) DeleteIP(addr net.IP, port types.L4Port) error {
|
||||
if addr == nil {
|
||||
return fmt.Errorf("IP address cannot be nil")
|
||||
@@ -87,7 +127,7 @@ func (l *listIPWithPort) DeleteIP(addr net.IP, port types.L4Port) error {
|
||||
return l.listIPv6.DeleteElement(element)
|
||||
}
|
||||
|
||||
func (l *listIPWithPort) AddRuleToChain(chainAddRuleFunc func(expr ...string) error, action string) error {
|
||||
func (l *listIPWithPort) AddRuleToChain(chainAddRuleFunc rule.AddFunc, action string) error {
|
||||
rule := "ip saddr . meta l4proto . th dport @" + l.listIPv4.Name() + " " + action
|
||||
if err := chainAddRuleFunc(rule); err != nil {
|
||||
return err
|
||||
@@ -0,0 +1,59 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/rule"
|
||||
)
|
||||
|
||||
type Chain interface {
|
||||
AddRule(expr ...string) error
|
||||
AddRuleIn(AddRuleFunc rule.AddFunc) error
|
||||
AddRuleOut(AddRuleFunc rule.AddFunc) error
|
||||
}
|
||||
|
||||
type batchChain struct {
|
||||
builder nft.BatchBuilder
|
||||
family family.Type
|
||||
table string
|
||||
chain string
|
||||
}
|
||||
|
||||
func NewBatchChain(builder nft.BatchBuilder, family family.Type, table string, chain string) (Chain, error) {
|
||||
if err := builder.Chain().Add(family, table, chain, nftChain.TypeNone); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &batchChain{
|
||||
builder: builder,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewBatchChainWithOptions(builder nft.BatchBuilder, family family.Type, table string, chain string, baseChain nftChain.ChainOptions) (Chain, error) {
|
||||
if err := builder.Chain().Add(family, table, chain, baseChain); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &batchChain{
|
||||
builder: builder,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *batchChain) AddRule(expr ...string) error {
|
||||
return b.builder.Rule().Add(b.family, b.table, b.chain, expr...)
|
||||
}
|
||||
|
||||
func (b *batchChain) AddRuleIn(AddRuleFunc rule.AddFunc) error {
|
||||
return AddRuleFunc("iifname != \"lo\" counter jump " + b.chain)
|
||||
}
|
||||
|
||||
func (b *batchChain) AddRuleOut(AddRuleFunc rule.AddFunc) error {
|
||||
return AddRuleFunc("oifname != \"lo\" counter jump " + b.chain)
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/data"
|
||||
)
|
||||
|
||||
type Docker interface {
|
||||
Name() string
|
||||
AddRule(builder nft.BatchBuilder, expr ...string) error
|
||||
JumpTo(builder nft.BatchBuilder, chain Docker, rule string, comment string) error
|
||||
ListRules() ([]data.Rule, error)
|
||||
RemoveRuleByHandle(builder nft.BatchBuilder, handle uint64) error
|
||||
Clear(builder nft.BatchBuilder) error
|
||||
}
|
||||
|
||||
type docker struct {
|
||||
nft nft.NFT
|
||||
family family.Type
|
||||
table string
|
||||
chain string
|
||||
}
|
||||
|
||||
func NewDocker(nft nft.NFT, family family.Type, table, chain string) Docker {
|
||||
return &docker{
|
||||
nft: nft,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *docker) Name() string {
|
||||
return d.chain
|
||||
}
|
||||
|
||||
func (d *docker) AddRule(builder nft.BatchBuilder, expr ...string) error {
|
||||
return builder.Rule().Add(d.family, d.table, d.chain, expr...)
|
||||
}
|
||||
|
||||
func (d *docker) JumpTo(builder nft.BatchBuilder, chain Docker, rule string, comment string) error {
|
||||
args := []string{rule, "jump", d.chain, comment}
|
||||
return chain.AddRule(builder, args...)
|
||||
}
|
||||
|
||||
func (d *docker) ListRules() ([]data.Rule, error) {
|
||||
args := []string{"-a", "-j", "list", "chain", d.family.String(), d.table, d.chain}
|
||||
jsonData, err := d.nft.Command().RunWithOutput(args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var output data.NftOutput
|
||||
if err := json.Unmarshal([]byte(jsonData), &output); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var rules []data.Rule
|
||||
for _, el := range output.Nftables {
|
||||
if el.Rule != nil {
|
||||
rules = append(rules, *el.Rule)
|
||||
}
|
||||
}
|
||||
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
func (d *docker) RemoveRuleByHandle(builder nft.BatchBuilder, handle uint64) error {
|
||||
return builder.Rule().Delete(d.family, d.table, d.chain, handle)
|
||||
}
|
||||
|
||||
func (d *docker) Clear(builder nft.BatchBuilder) error {
|
||||
return builder.Chain().Clear(d.family, d.table, d.chain)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
func NewBatchForward(builder nft.BatchBuilder, family family.Type, table string, chain string, defaultAllow bool, priority int) (Chain, error) {
|
||||
policy := nftChain.PolicyDrop
|
||||
if defaultAllow {
|
||||
policy = nftChain.PolicyAccept
|
||||
}
|
||||
|
||||
baseChain := nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeFilter,
|
||||
Hook: nftChain.HookForward,
|
||||
Priority: int32(priority),
|
||||
Policy: policy,
|
||||
Device: "",
|
||||
}
|
||||
|
||||
if err := builder.Chain().Add(family, table, chain, baseChain); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &batchChain{
|
||||
builder: builder,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
func NewBatchInput(builder nft.BatchBuilder, family family.Type, table string, chain string, defaultAllow bool, priority int) (Chain, error) {
|
||||
policy := nftChain.PolicyDrop
|
||||
if defaultAllow {
|
||||
policy = nftChain.PolicyAccept
|
||||
}
|
||||
|
||||
baseChain := nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeFilter,
|
||||
Hook: nftChain.HookInput,
|
||||
Priority: int32(priority),
|
||||
Policy: policy,
|
||||
Device: "",
|
||||
}
|
||||
|
||||
if err := builder.Chain().Add(family, table, chain, baseChain); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &batchChain{
|
||||
builder: builder,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
func NewBatchOutput(builder nft.BatchBuilder, family family.Type, table string, chain string, defaultAllow bool, priority int) (Chain, error) {
|
||||
policy := nftChain.PolicyDrop
|
||||
if defaultAllow {
|
||||
policy = nftChain.PolicyAccept
|
||||
}
|
||||
|
||||
baseChain := nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeFilter,
|
||||
Hook: nftChain.HookOutput,
|
||||
Priority: int32(priority),
|
||||
Policy: policy,
|
||||
Device: "",
|
||||
}
|
||||
|
||||
if err := builder.Chain().Add(family, table, chain, baseChain); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &batchChain{
|
||||
builder: builder,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package chain
|
||||
|
||||
import "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/rule"
|
||||
|
||||
type PacketFilter interface {
|
||||
AddRuleIn(AddRuleFunc rule.AddFunc) error
|
||||
AddRuleOut(AddRuleFunc rule.AddFunc) error
|
||||
}
|
||||
|
||||
type packetFilter struct {
|
||||
chainName string
|
||||
}
|
||||
|
||||
// NewPacketFilter drop-out-of-order packets and packets in an INVALID state in nftables connection tracking.
|
||||
func NewPacketFilter(chainName string) PacketFilter {
|
||||
return &packetFilter{
|
||||
chainName: chainName,
|
||||
}
|
||||
}
|
||||
|
||||
func (pf *packetFilter) AddRuleIn(addRuleFunc rule.AddFunc) error {
|
||||
return addRuleFunc("iifname != \"lo\" meta l4proto tcp counter jump " + pf.chainName)
|
||||
}
|
||||
|
||||
func (pf *packetFilter) AddRuleOut(addRuleFunc rule.AddFunc) error {
|
||||
return addRuleFunc("oifname != \"lo\" meta l4proto tcp counter jump " + pf.chainName)
|
||||
}
|
||||
|
||||
type packetFilterFalse struct{}
|
||||
|
||||
// NewPacketFilterFalse returns a PacketFilter that does nothing.
|
||||
func NewPacketFilterFalse() PacketFilter {
|
||||
return &packetFilterFalse{}
|
||||
}
|
||||
|
||||
func (pf *packetFilterFalse) AddRuleIn(_ rule.AddFunc) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pf *packetFilterFalse) AddRuleOut(_ rule.AddFunc) error {
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/block"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/rule"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/types"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/pkg/ip"
|
||||
)
|
||||
|
||||
type PortKnocking interface {
|
||||
AddFirstStageRule(
|
||||
name string,
|
||||
ipVersion ip.Version,
|
||||
l4Port types.L4Port,
|
||||
timeout uint32,
|
||||
action types.KnockAction,
|
||||
) error
|
||||
AddNextStageRule(
|
||||
prevName, nextName string,
|
||||
ipVersion ip.Version,
|
||||
l4Port types.L4Port,
|
||||
timeout uint32,
|
||||
action types.KnockAction,
|
||||
) error
|
||||
AddRuleIn(AddRuleFunc rule.AddFunc) error
|
||||
}
|
||||
|
||||
type portKnocking struct {
|
||||
chain Chain
|
||||
sets block.Sets
|
||||
}
|
||||
|
||||
func NewBatchPortKnocking(builder nft.BatchBuilder, family family.Type, table string, chain string) (PortKnocking, error) {
|
||||
batchChain, err := NewBatchChain(builder, family, table, chain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &portKnocking{
|
||||
chain: batchChain,
|
||||
sets: block.NewBatchSet(builder, family, table),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (k *portKnocking) AddRuleIn(AddRuleFunc rule.AddFunc) error {
|
||||
return k.chain.AddRuleIn(AddRuleFunc)
|
||||
}
|
||||
|
||||
func (k *portKnocking) AddFirstStageRule(
|
||||
name string,
|
||||
ipVersion ip.Version,
|
||||
l4Port types.L4Port,
|
||||
timeout uint32,
|
||||
action types.KnockAction,
|
||||
) error {
|
||||
if err := k.newPortKnocking(name, ipVersion, timeout); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
expr := []string{
|
||||
l4Port.ProtocolString(), "dport", l4Port.NumberString(), "add", "@" + name,
|
||||
"{", ipVersion.ToNft(), "saddr timeout", strconv.Itoa(int(timeout)) + "s", "}", action.String(),
|
||||
}
|
||||
return k.chain.AddRule(expr...)
|
||||
}
|
||||
|
||||
func (k *portKnocking) AddNextStageRule(
|
||||
prevName, nextName string,
|
||||
ipVersion ip.Version,
|
||||
l4Port types.L4Port,
|
||||
timeout uint32,
|
||||
action types.KnockAction,
|
||||
) error {
|
||||
if err := k.newPortKnocking(nextName, ipVersion, timeout); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
expr := []string{
|
||||
ipVersion.ToNft(), "saddr", "@" + prevName,
|
||||
l4Port.ProtocolString(), "dport", l4Port.NumberString(), "add", "@" + nextName,
|
||||
"{", ipVersion.ToNft(), "saddr}", action.String(),
|
||||
}
|
||||
return k.chain.AddRule(expr...)
|
||||
}
|
||||
|
||||
func (k *portKnocking) newPortKnocking(name string, ipVersion ip.Version, timeout uint32) error {
|
||||
params := []string{"type", ipVersion.ToNftForSet() + ";", "flags timeout; timeout", strconv.Itoa(int(timeout)) + "s;"}
|
||||
return k.sets.Add(name, strings.Join(params, " "))
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package data
|
||||
|
||||
type NftOutput struct {
|
||||
Nftables []NftElement `json:"nftables"`
|
||||
}
|
||||
type NftElement struct {
|
||||
Rule *Rule `json:"rule,omitempty"`
|
||||
}
|
||||
|
||||
type Rule struct {
|
||||
Handle uint64 `json:"handle"`
|
||||
Comment string `json:"comment"`
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package nft
|
||||
|
||||
import (
|
||||
nftables "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
)
|
||||
|
||||
type NFT interface {
|
||||
NewBuildBatch() (nft.BatchBuilder, error)
|
||||
RunBatch(batchBuilder nft.BatchBuilder) error
|
||||
RunBatchAndMoveFile(batchBuilder nft.BatchBuilder, fileDir string) error
|
||||
NFT() nft.NFT
|
||||
}
|
||||
|
||||
type nftImpl struct {
|
||||
nft nft.NFT
|
||||
tmpDir string
|
||||
}
|
||||
|
||||
func New(nft nft.NFT, tmpDir string) NFT {
|
||||
return &nftImpl{
|
||||
nft: nft,
|
||||
tmpDir: tmpDir,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *nftImpl) NFT() nft.NFT {
|
||||
return n.nft
|
||||
}
|
||||
|
||||
func (n *nftImpl) NewBuildBatch() (nft.BatchBuilder, error) {
|
||||
return nftables.NewBatchBuilder(n.tmpDir)
|
||||
}
|
||||
|
||||
func (n *nftImpl) RunBatch(batchBuilder nft.BatchBuilder) error {
|
||||
batch := batchBuilder.Build()
|
||||
defer func() {
|
||||
_ = batch.Close()
|
||||
}()
|
||||
|
||||
return n.nft.ExecuteBatchAfterCheck(batch)
|
||||
}
|
||||
|
||||
func (n *nftImpl) RunBatchAndMoveFile(batchBuilder nft.BatchBuilder, fileDir string) error {
|
||||
batch := batchBuilder.Build()
|
||||
defer func() {
|
||||
_ = batch.Close()
|
||||
}()
|
||||
if err := n.nft.ExecuteBatchAfterCheck(batch); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return batch.MoveFile(fileDir)
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package rule
|
||||
|
||||
import "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/config"
|
||||
|
||||
type AddFunc func(expr ...string) error
|
||||
|
||||
func InputAddIP(addRuleFunc AddFunc, config config.ConfigIP, ipMatch string) error {
|
||||
rule := ipMatch + " saddr " + config.IP + " iifname != \"lo\""
|
||||
if !config.OnlyIP {
|
||||
rule += " " + config.Port.ProtocolString() + " dport " + config.Port.NumberString()
|
||||
}
|
||||
if config.LimitRate != "" {
|
||||
rule += " limit rate " + config.LimitRate
|
||||
}
|
||||
rule += " counter " + config.Action.String()
|
||||
return addRuleFunc(rule)
|
||||
}
|
||||
|
||||
func OutputAddIP(addRuleFunc AddFunc, config config.ConfigIP, ipMatch string) error {
|
||||
rule := ipMatch + " daddr " + config.IP + " oifname != \"lo\""
|
||||
if !config.OnlyIP {
|
||||
rule += " " + config.Port.ProtocolString() + " dport " + config.Port.NumberString()
|
||||
}
|
||||
if config.LimitRate != "" {
|
||||
rule += " limit rate " + config.LimitRate
|
||||
}
|
||||
rule += " counter " + config.Action.String()
|
||||
return addRuleFunc(rule)
|
||||
}
|
||||
|
||||
func ForwardAddIP(addRuleFunc AddFunc, config config.ConfigIP, ipMatch string) error {
|
||||
rule := ipMatch + " saddr " + config.IP + " iifname != \"lo\""
|
||||
|
||||
// There, during routing, the port changes and then the IP blocking rule will not work.
|
||||
//if !config.OnlyIP {
|
||||
// rule += " " + config.Protocol.String() + " dport " + strconv.Itoa(int(config.Port))
|
||||
//}
|
||||
|
||||
rule += " counter " + config.Action.String()
|
||||
if err := addRuleFunc(rule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package table
|
||||
|
||||
import (
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/firewall"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/block"
|
||||
)
|
||||
|
||||
type Table interface {
|
||||
Clear() error
|
||||
DockerChains() firewall.NFTDockerChains
|
||||
BlockList() BlockList
|
||||
}
|
||||
|
||||
type BlockList interface {
|
||||
ListIP() block.ListIP
|
||||
ListIPWithPort() block.ListIPWithPort
|
||||
Blocks() map[string]block.Blocklist
|
||||
}
|
||||
|
||||
type table struct {
|
||||
nft nft.NFT
|
||||
family family.Type
|
||||
name string
|
||||
|
||||
dockerChains firewall.NFTDockerChains
|
||||
blockList BlockList
|
||||
}
|
||||
|
||||
func New(
|
||||
nft nft.NFT, family family.Type, name string,
|
||||
blockList BlockList, dockerChains firewall.NFTDockerChains,
|
||||
) Table {
|
||||
return &table{
|
||||
nft: nft,
|
||||
family: family,
|
||||
name: name,
|
||||
|
||||
dockerChains: dockerChains,
|
||||
blockList: blockList,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *table) Clear() error {
|
||||
// clear does not clean completely
|
||||
return t.nft.NFT().Table().Delete(t.family, t.name)
|
||||
}
|
||||
|
||||
func (t *table) DockerChains() firewall.NFTDockerChains {
|
||||
return t.dockerChains
|
||||
}
|
||||
|
||||
func (t *table) BlockList() BlockList {
|
||||
return t.blockList
|
||||
}
|
||||
|
||||
type blockList struct {
|
||||
listIP block.ListIP
|
||||
listIPWithPort block.ListIPWithPort
|
||||
blocks map[string]block.Blocklist
|
||||
}
|
||||
|
||||
func NewBlockList(listIP block.ListIP, listIPWithPort block.ListIPWithPort, blocks map[string]block.Blocklist) BlockList {
|
||||
return &blockList{
|
||||
listIP: listIP,
|
||||
listIPWithPort: listIPWithPort,
|
||||
blocks: blocks,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *blockList) ListIP() block.ListIP {
|
||||
return b.listIP
|
||||
}
|
||||
|
||||
func (b *blockList) ListIPWithPort() block.ListIPWithPort {
|
||||
return b.listIPWithPort
|
||||
}
|
||||
|
||||
func (b *blockList) Blocks() map[string]block.Blocklist {
|
||||
return b.blocks
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package reload
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/block"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/chain"
|
||||
nftTable "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/table"
|
||||
)
|
||||
|
||||
func (r *reload) blockList(builder nft.BatchBuilder, beforeLocalInput chain.Chain, blocks map[string]block.Blocklist) (nftTable.BlockList, error) {
|
||||
listBlockedIP, err := block.NewListIP(r.nft, builder, r.table.family, r.table.name, blockedIP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := listBlockedIP.AddRuleToChain(beforeLocalInput.AddRule, "drop"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
listBlockedIPWithPort, err := block.NewListIPWithPort(r.nft, builder, r.table.family, r.table.name, blockedIPWithPort)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := listBlockedIPWithPort.AddRuleToChain(beforeLocalInput.AddRule, "drop"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nftTable.NewBlockList(listBlockedIP, listBlockedIPWithPort, blocks), nil
|
||||
}
|
||||
|
||||
func (r *reload) moduleBlockList(builder nft.BatchBuilder, afterLocalInput chain.Chain, blockListNames []string) (blocks map[string]block.Blocklist, err error) {
|
||||
r.logger.Debug("Reload blocklist")
|
||||
blocks = make(map[string]block.Blocklist)
|
||||
for _, blockListName := range blockListNames {
|
||||
if blockListName == "" {
|
||||
continue
|
||||
}
|
||||
r.logger.Debug(fmt.Sprintf("Reload blocklist from %s", blockListName))
|
||||
blockList, err := block.NewBlocklist(r.nft, builder, r.table.family, r.table.name, getBlocklistName(blockListName))
|
||||
if err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to create blocklist: %s", err))
|
||||
continue
|
||||
}
|
||||
if err := blockList.AddRuleToChain(afterLocalInput.AddRule, "drop"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule to chain: %s", err))
|
||||
continue
|
||||
}
|
||||
blocks[blockListName] = blockList
|
||||
}
|
||||
|
||||
return blocks, nil
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package reload
|
||||
|
||||
import (
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/firewall"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/chain"
|
||||
)
|
||||
|
||||
func (r *reload) docker(builder nft.BatchBuilder, batchForward chain.Chain, dockerChains firewall.NFTDockerChains) error {
|
||||
r.logger.Debug("Reload docker rules")
|
||||
|
||||
for _, dockerChain := range dockerChains.List() {
|
||||
if err := r.addChain(builder, dockerChain.Name(), nftChain.TypeNone); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := batchForward.AddRule("jump docker_forward_filter"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return r.dockerPrerouting(builder, dockerChains)
|
||||
}
|
||||
|
||||
func (r *reload) dockerPrerouting(builder nft.BatchBuilder, dockerChains firewall.NFTDockerChains) error {
|
||||
var rule []string
|
||||
|
||||
preroutingNat, err := r.addChainWithReturn(builder, "prerouting_nat", nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeNat,
|
||||
Hook: nftChain.HookPrerouting,
|
||||
Priority: -100,
|
||||
Policy: nftChain.PolicyAccept,
|
||||
Device: "",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rule = []string{"fib daddr type local counter jump ", dockerChains.DockerNat().Name()}
|
||||
if err := preroutingNat.AddRule(rule...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
preroutingFilter, err := r.addChainWithReturn(builder, "prerouting_filter", nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeFilter,
|
||||
Hook: nftChain.HookPrerouting,
|
||||
Priority: -300,
|
||||
Policy: nftChain.PolicyAccept,
|
||||
Device: "",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rule = []string{"jump ", dockerChains.PreroutingFilter().Name()}
|
||||
if err := preroutingFilter.AddRule(rule...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
outputNat, err := r.addChainWithReturn(builder, "output_nat", nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeNat,
|
||||
Hook: nftChain.HookOutput,
|
||||
Priority: -100,
|
||||
Policy: nftChain.PolicyAccept,
|
||||
Device: "",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rule = []string{"ip daddr != 127.0.0.0/8 fib daddr type local counter jump ", dockerChains.DockerNat().Name()}
|
||||
if err := outputNat.AddRule(rule...); err != nil {
|
||||
return err
|
||||
}
|
||||
rule = []string{"ip6 daddr != ::1 fib daddr type local counter jump ", dockerChains.DockerNat().Name()}
|
||||
if err := outputNat.AddRule(rule...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
postroutingNat, err := r.addChainWithReturn(builder, "postrouting_nat", nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeNat,
|
||||
Hook: nftChain.HookPostrouting,
|
||||
Priority: 300,
|
||||
Policy: nftChain.PolicyAccept,
|
||||
Device: "",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rule = []string{"jump ", dockerChains.PostroutingNat().Name()}
|
||||
if err := postroutingNat.AddRule(rule...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package reload
|
||||
|
||||
import (
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/firewall"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/rule"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/types"
|
||||
)
|
||||
|
||||
func (r *reload) forward(builder nft.BatchBuilder, dockerChains firewall.NFTDockerChains) error {
|
||||
r.logger.Debug("Reloading forward chain")
|
||||
|
||||
batchForward, err := chain.NewBatchForward(
|
||||
builder,
|
||||
r.table.family,
|
||||
r.table.name,
|
||||
r.config.MetadataNaming.ChainForwardName,
|
||||
r.config.Policy.DefaultAllowForward,
|
||||
r.config.Policy.ForwardPriority,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
localForward, err := chain.NewBatchChain(builder, r.table.family, r.table.name, "local-forward")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.forwardAddIPs(batchForward, localForward); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if r.config.Options.DockerSupport && dockerChains != nil {
|
||||
if err := r.docker(builder, batchForward, dockerChains); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if r.config.Policy.DefaultAllowForward == false {
|
||||
drop := r.config.Policy.ForwardDrop.String()
|
||||
if err := batchForward.AddRule(drop); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *reload) forwardAddIPs(batchForward chain.Chain, localForward chain.Chain) error {
|
||||
if err := localForward.AddRuleIn(batchForward.AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, ipConfig := range r.config.IP4.InIPs {
|
||||
if ipConfig.Action != types.ActionDrop && ipConfig.Action != types.ActionReject {
|
||||
continue
|
||||
}
|
||||
if err := rule.ForwardAddIP(localForward.AddRule, ipConfig, "ip"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !r.config.IP6.Enable {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, ipConfig := range r.config.IP6.InIPs {
|
||||
if ipConfig.Action != types.ActionDrop && ipConfig.Action != types.ActionReject {
|
||||
continue
|
||||
}
|
||||
if err := rule.ForwardAddIP(localForward.AddRule, ipConfig, "ip6"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
package reload
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/rule"
|
||||
nftTable "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/table"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/pkg"
|
||||
)
|
||||
|
||||
func (r *reload) input(builder nft.BatchBuilder, packetfilter chain.PacketFilter, blockListNames []string) (nftTable.BlockList, error) {
|
||||
r.logger.Debug("Reloading input chain")
|
||||
|
||||
batchInput, err := chain.NewBatchInput(
|
||||
builder,
|
||||
r.table.family,
|
||||
r.table.name,
|
||||
r.config.MetadataNaming.ChainInputName,
|
||||
r.config.Policy.DefaultAllowInput,
|
||||
r.config.Policy.InputPriority,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.reloadInputDnsNs(batchInput); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := batchInput.AddRule("iifname lo counter accept"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
beforeLocalInput, err := chain.NewBatchChain(builder, r.table.family, r.table.name, "before-local-input")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := beforeLocalInput.AddRuleIn(batchInput.AddRule); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
localInput, err := chain.NewBatchChain(builder, r.table.family, r.table.name, "local-input")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := r.inputAddIPs(builder, batchInput, localInput); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
afterLocalInput, err := chain.NewBatchChain(builder, r.table.family, r.table.name, "after-local-input")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := afterLocalInput.AddRuleIn(batchInput.AddRule); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := packetfilter.AddRuleIn(batchInput.AddRule); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.inputICMP(batchInput); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := batchInput.AddRule("iifname != \"lo\" ct state related,established counter accept"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.inputPorts(batchInput); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if r.config.Policy.DefaultAllowInput == false {
|
||||
drop := r.config.Policy.InputDrop.String()
|
||||
if err := batchInput.AddRule("iifname != \"lo\" " + drop); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
blocks, err := r.moduleBlockList(builder, afterLocalInput, blockListNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.blockList(builder, beforeLocalInput, blocks)
|
||||
}
|
||||
|
||||
func (r *reload) reloadInputDnsNs(batchInput chain.Chain) error {
|
||||
if r.config.Options.DnsStrictNs {
|
||||
return nil
|
||||
}
|
||||
|
||||
addresses, err := pkg.Resolv.Addresses()
|
||||
if err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to get nameservers: %s", err))
|
||||
return nil
|
||||
}
|
||||
for _, addr := range addresses {
|
||||
ip := net.ParseIP(addr)
|
||||
if ip == nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to parse nameserver address: %s", addr))
|
||||
continue
|
||||
}
|
||||
if ip.To4() != nil {
|
||||
if err := batchInput.AddRule("ip saddr " + addr + " iifname != \"lo\" tcp dport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := batchInput.AddRule("ip saddr " + addr + " iifname != \"lo\" udp dport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := batchInput.AddRule("ip saddr " + addr + " iifname != \"lo\" tcp sport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := batchInput.AddRule("ip saddr " + addr + " iifname != \"lo\" udp sport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if ip.To16() != nil {
|
||||
if !r.config.IP6.Enable {
|
||||
r.logger.Warn(fmt.Sprintf("IPv6 is disabled, skipping nameserver address: %s", addr))
|
||||
continue
|
||||
}
|
||||
if err := batchInput.AddRule("ip6 saddr " + addr + " iifname != \"lo\" tcp dport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := batchInput.AddRule("ip6 saddr " + addr + " iifname != \"lo\" udp dport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := batchInput.AddRule("ip6 saddr " + addr + " iifname != \"lo\" tcp sport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := batchInput.AddRule("ip6 saddr " + addr + " iifname != \"lo\" udp sport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
r.logger.Error(fmt.Sprintf("Failed to parse nameserver address: %s", addr))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *reload) inputAddIPs(builder nft.BatchBuilder, batchInput chain.Chain, localInput chain.Chain) error {
|
||||
if err := localInput.AddRuleIn(batchInput.AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.inputPortKnocking(builder, localInput); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, ipConfig := range r.config.IP4.InIPs {
|
||||
if err := rule.InputAddIP(localInput.AddRule, ipConfig, "ip"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !r.config.IP6.Enable {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, ipConfig := range r.config.IP6.InIPs {
|
||||
if err := rule.InputAddIP(localInput.AddRule, ipConfig, "ip6"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *reload) inputPortKnocking(builder nft.BatchBuilder, localInput chain.Chain) error {
|
||||
if len(r.config.PortKnocking) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
portKnocking, err := chain.NewBatchPortKnocking(builder, r.table.family, r.table.name, "port_knocking")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, portKnockingConfig := range r.config.PortKnocking {
|
||||
var knockName, prevKnockName string
|
||||
for index, knock := range portKnockingConfig.Knocks {
|
||||
prevKnockName = knockName
|
||||
knockName = fmt.Sprintf("knock_%s_%d", portKnockingConfig.Name, index)
|
||||
if index == 0 {
|
||||
if err := portKnocking.AddFirstStageRule(knockName, portKnockingConfig.IPVersion, knock.Port, knock.Timeout, knock.Action); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if err := portKnocking.AddNextStageRule(prevKnockName, knockName, portKnockingConfig.IPVersion, knock.Port, knock.Timeout, knock.Action); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
expr := []string{
|
||||
portKnockingConfig.IPVersion.ToNft(), "saddr", "@" + knockName,
|
||||
portKnockingConfig.Port.ProtocolString(), "dport", portKnockingConfig.Port.NumberString(), "accept",
|
||||
}
|
||||
if err := localInput.AddRule(expr...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := portKnocking.AddRuleIn(localInput.AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *reload) inputICMP(batchInput chain.Chain) error {
|
||||
drop := r.config.Policy.InputDrop.String()
|
||||
if r.config.IP4.IcmpIn == false {
|
||||
if err := batchInput.AddRule("iifname != \"lo\" ip protocol icmp icmp type echo-request counter " + drop); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.inputICMPAfter(batchInput)
|
||||
}
|
||||
|
||||
if r.config.IP4.IcmpInRate == "0" {
|
||||
return r.inputICMPAfter(batchInput)
|
||||
}
|
||||
|
||||
if err := batchInput.AddRule("iifname != \"lo\" ip protocol icmp icmp type echo-request limit rate " + r.config.IP4.IcmpInRate + " counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchInput.AddRule("iifname != \"lo\" ip protocol icmp icmp type echo-request counter " + drop); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return r.inputICMPAfter(batchInput)
|
||||
}
|
||||
|
||||
func (r *reload) inputICMPAfter(batchInput chain.Chain) error {
|
||||
if r.config.IP4.IcmpTimestampDrop == true {
|
||||
drop := r.config.Policy.InputDrop.String()
|
||||
if err := batchInput.AddRule("iifname != \"lo\" ip protocol icmp icmp type timestamp-request " + drop); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := batchInput.AddRule("iifname != \"lo\" ip protocol icmp counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if r.config.IP6.Enable {
|
||||
if r.config.IP6.IcmpStrict {
|
||||
return r.inputICMP6Strict(batchInput)
|
||||
} else if err := batchInput.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *reload) inputICMP6Strict(batchInput chain.Chain) error {
|
||||
if err := batchInput.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type destination-unreachable counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchInput.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type packet-too-big counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchInput.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type time-exceeded counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchInput.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type parameter-problem counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchInput.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type echo-request counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchInput.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type echo-reply counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchInput.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type nd-router-advert ip6 hoplimit 255 counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchInput.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type nd-neighbor-solicit ip6 hoplimit 255 counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchInput.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type nd-neighbor-advert ip6 hoplimit 255 counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchInput.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type nd-redirect ip6 hoplimit 255 counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *reload) inputPorts(batchInput chain.Chain) error {
|
||||
for _, port := range r.config.InPorts {
|
||||
protocol := port.Port.ProtocolString()
|
||||
number := port.Port.NumberString()
|
||||
|
||||
baseRule := "iifname != \"lo\" meta l4proto " + protocol + " ct state new " + protocol + " dport " + number
|
||||
|
||||
if port.LimitRate != "" {
|
||||
addRule := baseRule + " limit rate " + port.LimitRate + " counter " + port.Action.String()
|
||||
if err := batchInput.AddRule(addRule); err != nil {
|
||||
return err
|
||||
}
|
||||
addRuleDrop := baseRule + " counter " + r.config.Policy.InputDrop.String()
|
||||
if err := batchInput.AddRule(addRuleDrop); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
addRule := baseRule + " counter " + port.Action.String()
|
||||
if err := batchInput.AddRule(addRule); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
package reload
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/rule"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/pkg"
|
||||
)
|
||||
|
||||
func (r *reload) output(builder nft.BatchBuilder, packetfilter chain.PacketFilter) error {
|
||||
r.logger.Debug("Reloading output chain")
|
||||
|
||||
batchOutput, err := chain.NewBatchOutput(
|
||||
builder,
|
||||
r.table.family,
|
||||
r.table.name,
|
||||
r.config.MetadataNaming.ChainOutputName,
|
||||
r.config.Policy.DefaultAllowOutput,
|
||||
r.config.Policy.OutputPriority,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.outputDnsNs(batchOutput); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.outputDns(batchOutput); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchOutput.AddRule("oifname lo counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
localOutput, err := chain.NewBatchChain(builder, r.table.family, r.table.name, "local-output")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.outputAddIPs(batchOutput, localOutput); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := packetfilter.AddRuleOut(batchOutput.AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.outputICMP(batchOutput); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := batchOutput.AddRule("oifname != \"lo\" ct state related,established counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.outputPorts(batchOutput); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if r.config.Policy.DefaultAllowOutput == false {
|
||||
drop := r.config.Policy.OutputDrop.String()
|
||||
if err := batchOutput.AddRule("oifname != \"lo\" " + drop); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *reload) outputDnsNs(batchOutput chain.Chain) error {
|
||||
if r.config.Options.DnsStrictNs {
|
||||
return nil
|
||||
}
|
||||
|
||||
addresses, err := pkg.Resolv.Addresses()
|
||||
if err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to get nameservers: %s", err))
|
||||
return nil
|
||||
}
|
||||
for _, addr := range addresses {
|
||||
ip := net.ParseIP(addr)
|
||||
if ip == nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to parse nameserver address: %s", addr))
|
||||
continue
|
||||
}
|
||||
if ip.To4() != nil {
|
||||
if err := batchOutput.AddRule("ip daddr " + addr + " oifname != \"lo\" tcp dport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := batchOutput.AddRule("ip daddr " + addr + " oifname != \"lo\" udp dport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := batchOutput.AddRule("ip daddr " + addr + " oifname != \"lo\" tcp sport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := batchOutput.AddRule("ip daddr " + addr + " oifname != \"lo\" udp sport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if ip.To16() != nil {
|
||||
if err := batchOutput.AddRule("ip6 daddr " + addr + " oifname != \"lo\" tcp dport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := batchOutput.AddRule("ip6 daddr " + addr + " oifname != \"lo\" udp dport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := batchOutput.AddRule("ip6 daddr " + addr + " oifname != \"lo\" tcp sport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := batchOutput.AddRule("ip6 daddr " + addr + " oifname != \"lo\" udp sport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
r.logger.Error(fmt.Sprintf("Failed to parse nameserver address: %s", addr))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *reload) outputDns(batchOutput chain.Chain) error {
|
||||
if r.config.Options.DnsStrict {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := batchOutput.AddRule("oifname != \"lo\" tcp dport 53 counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchOutput.AddRule("oifname != \"lo\" udp dport 53 counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchOutput.AddRule("oifname != \"lo\" tcp sport 53 counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchOutput.AddRule("oifname != \"lo\" udp sport 53 counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *reload) outputAddIPs(batchOutput chain.Chain, localOutput chain.Chain) error {
|
||||
if err := localOutput.AddRuleOut(batchOutput.AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, ipConfig := range r.config.IP4.OutIPs {
|
||||
if err := rule.OutputAddIP(localOutput.AddRule, ipConfig, "ip"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !r.config.IP6.Enable {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, ipConfig := range r.config.IP6.OutIPs {
|
||||
if err := rule.OutputAddIP(localOutput.AddRule, ipConfig, "ip6"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *reload) outputICMP(batchOutput chain.Chain) error {
|
||||
drop := r.config.Policy.OutputDrop.String()
|
||||
if r.config.IP4.IcmpOut == false {
|
||||
if err := batchOutput.AddRule("oifname != \"lo\" ip protocol icmp icmp type echo-request counter " + drop); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.outputICMPAfter(batchOutput)
|
||||
}
|
||||
|
||||
if r.config.IP4.IcmpOutRate == "0" {
|
||||
return r.outputICMPAfter(batchOutput)
|
||||
}
|
||||
|
||||
if err := batchOutput.AddRule("oifname != \"lo\" ip protocol icmp icmp type echo-request limit rate " + r.config.IP4.IcmpInRate + " counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchOutput.AddRule("oifname != \"lo\" ip protocol icmp icmp type echo-request counter " + drop); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return r.outputICMPAfter(batchOutput)
|
||||
}
|
||||
|
||||
func (r *reload) outputICMPAfter(batchOutput chain.Chain) error {
|
||||
if r.config.IP4.IcmpTimestampDrop == true {
|
||||
drop := r.config.Policy.OutputDrop.String()
|
||||
if err := batchOutput.AddRule("oifname != \"lo\" ip protocol icmp icmp type timestamp-request " + drop); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := batchOutput.AddRule("oifname != \"lo\" ip protocol icmp counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *reload) outputPorts(batchOutput chain.Chain) error {
|
||||
for _, port := range r.config.OutPorts {
|
||||
protocol := port.Port.ProtocolString()
|
||||
number := port.Port.NumberString()
|
||||
baseRule := "oifname != \"lo\" meta l4proto " + protocol + " ct state new " + protocol + " dport " + number
|
||||
|
||||
if port.LimitRate != "" {
|
||||
addRule := baseRule + " limit rate " + port.LimitRate + " counter " + port.Action.String()
|
||||
if err := batchOutput.AddRule(addRule); err != nil {
|
||||
return err
|
||||
}
|
||||
addRuleDrop := baseRule + " counter " + r.config.Policy.InputDrop.String()
|
||||
if err := batchOutput.AddRule(addRuleDrop); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
addRule := baseRule + " counter " + port.Action.String()
|
||||
if err := batchOutput.AddRule(addRule); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package reload
|
||||
|
||||
import (
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/chain"
|
||||
)
|
||||
|
||||
func (r *reload) packetFilter(builder nft.BatchBuilder) (chain.PacketFilter, error) {
|
||||
if !r.config.Options.PacketFilter {
|
||||
return chain.NewPacketFilterFalse(), nil
|
||||
}
|
||||
|
||||
chainInvalidName := "INVALID"
|
||||
|
||||
chainName := "INVDROP"
|
||||
|
||||
if err := r.addChain(builder, chainName, nftChain.TypeNone); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := r.addRule(builder, chainName, "counter drop"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.addChain(builder, chainInvalidName, nftChain.TypeNone); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.addRule(builder, chainInvalidName, "ct state invalid counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.addRule(builder, chainInvalidName, "tcp flags ! fin,syn,rst,psh,ack,urg counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.addRule(builder, chainInvalidName, "tcp flags & (fin | syn | rst | psh | ack | urg) == fin | syn | rst | psh | ack | urg counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.addRule(builder, chainInvalidName, "tcp flags & (fin | syn) == fin | syn counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.addRule(builder, chainInvalidName, "tcp flags & (syn | rst) == syn | rst counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.addRule(builder, chainInvalidName, "tcp flags & (fin | rst) == fin | rst counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.addRule(builder, chainInvalidName, "tcp flags & (fin | ack) == fin counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.addRule(builder, chainInvalidName, "tcp flags & (psh | ack) == psh counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.addRule(builder, chainInvalidName, "tcp flags & (ack | urg) == urg counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.addRule(builder, chainInvalidName, "tcp flags & (fin | syn | rst | ack) != syn ct state new counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return chain.NewPacketFilter(chainInvalidName), nil
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
package reload
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/firewall"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/config"
|
||||
nftFirewall "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/block"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/chain"
|
||||
dataTable "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/table"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
)
|
||||
|
||||
const blockedIP = "block_ip"
|
||||
const blockedIPWithPort = "block_ip_with_port"
|
||||
|
||||
type Reload interface {
|
||||
RunWithCache(file string, isValidCacheFile bool, blockListNames []string) (dataTable.Table, error)
|
||||
Run(blockListNames []string) (dataTable.Table, error)
|
||||
}
|
||||
|
||||
type table struct {
|
||||
name string
|
||||
family family.Type
|
||||
}
|
||||
|
||||
type reload struct {
|
||||
nft nftFirewall.NFT
|
||||
logger log.Logger
|
||||
config *config.Config
|
||||
table *table
|
||||
}
|
||||
|
||||
func New(nft nftFirewall.NFT, logger log.Logger, config *config.Config) Reload {
|
||||
return &reload{
|
||||
nft: nft,
|
||||
logger: logger,
|
||||
config: config,
|
||||
table: &table{
|
||||
name: config.MetadataNaming.TableName,
|
||||
family: family.INET,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *reload) RunWithCache(file string, isValidCacheFile bool, blockListNames []string) (dataTable.Table, error) {
|
||||
if file == "" {
|
||||
r.logger.Warn("file is empty, using default reload")
|
||||
return r.Run(blockListNames)
|
||||
}
|
||||
if isValidCacheFile {
|
||||
r.logger.Debug("use cache file")
|
||||
table, err := r.loadCache(file, blockListNames)
|
||||
if err == nil {
|
||||
return table, nil
|
||||
}
|
||||
r.logger.Warn(fmt.Sprintf("failed to load cache file: %s", err))
|
||||
}
|
||||
|
||||
batchBuilder, err := r.nft.NewBuildBatch()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err := batchBuilder.Close(); err != nil {
|
||||
r.logger.Warn(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
table, err := r.reload(batchBuilder, blockListNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.nft.RunBatchAndMoveFile(batchBuilder, file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return table, nil
|
||||
}
|
||||
|
||||
func (r *reload) Run(blockListNames []string) (dataTable.Table, error) {
|
||||
batchBuilder, err := r.nft.NewBuildBatch()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err := batchBuilder.Close(); err != nil {
|
||||
r.logger.Warn(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
table, err := r.reload(batchBuilder, blockListNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.nft.RunBatch(batchBuilder); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return table, nil
|
||||
}
|
||||
|
||||
func (r *reload) reload(batchBuilder nft.BatchBuilder, blockListNames []string) (dataTable.Table, error) {
|
||||
var dockerChains firewall.NFTDockerChains
|
||||
if r.config.Options.DockerSupport {
|
||||
dockerChains = firewall.NewNFTChains(r.nft, r.table.family, r.table.name)
|
||||
}
|
||||
|
||||
if err := r.clear(batchBuilder); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
packetFilter, err := r.packetFilter(batchBuilder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blockList, err := r.input(batchBuilder, packetFilter, blockListNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.output(batchBuilder, packetFilter); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := r.forward(batchBuilder, dockerChains); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dataTable.New(
|
||||
r.nft, r.table.family, r.table.name,
|
||||
blockList, dockerChains,
|
||||
), nil
|
||||
}
|
||||
|
||||
func (r *reload) clear(builder nft.BatchBuilder) error {
|
||||
switch r.config.Options.ClearMode {
|
||||
case config.ClearModeGlobal:
|
||||
if err := builder.Clear(); err != nil {
|
||||
return fmt.Errorf("failed to clear global rules: %w", err)
|
||||
}
|
||||
if err := builder.Table().Add(r.table.family, r.table.name); err != nil {
|
||||
return fmt.Errorf("failed to add table: %w", err)
|
||||
}
|
||||
break
|
||||
case config.ClearModeOwn:
|
||||
if err := builder.Table().Add(r.table.family, r.table.name); err != nil {
|
||||
return fmt.Errorf("failed to add table: %w", err)
|
||||
}
|
||||
// clear does not clean completely
|
||||
if err := builder.Table().Delete(r.table.family, r.table.name); err != nil {
|
||||
return fmt.Errorf("failed to clear table: %w", err)
|
||||
}
|
||||
if err := builder.Table().Add(r.table.family, r.table.name); err != nil {
|
||||
return fmt.Errorf("failed to add table: %w", err)
|
||||
}
|
||||
break
|
||||
default:
|
||||
return fmt.Errorf("unknown clear mode: %d", r.config.Options.ClearMode)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *reload) loadCache(file string, blockListNames []string) (dataTable.Table, error) {
|
||||
args := []string{"-f", file}
|
||||
if err := r.nft.NFT().Command().Run(args...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var dockerChains firewall.NFTDockerChains
|
||||
if r.config.Options.DockerSupport {
|
||||
dockerChains = firewall.NewNFTChains(r.nft, r.table.family, r.table.name)
|
||||
}
|
||||
|
||||
blocks := make(map[string]block.Blocklist)
|
||||
for _, blockListName := range blockListNames {
|
||||
if blockListName == "" {
|
||||
continue
|
||||
}
|
||||
blockList := block.NewBlocklistWithoutCommand(r.nft, r.table.family, r.table.name, getBlocklistName(blockListName))
|
||||
blocks[blockListName] = blockList
|
||||
}
|
||||
|
||||
listBlockedIP := block.NewListIPWithoutCommand(r.nft, r.table.family, r.table.name, blockedIP)
|
||||
listBlockedIPWithPort := block.NewListIPWithPortWithoutCommand(r.nft, r.table.family, r.table.name, blockedIPWithPort)
|
||||
|
||||
tableBlocklist := dataTable.NewBlockList(listBlockedIP, listBlockedIPWithPort, blocks)
|
||||
return dataTable.New(
|
||||
r.nft, r.table.family, r.table.name,
|
||||
tableBlocklist, dockerChains,
|
||||
), nil
|
||||
}
|
||||
|
||||
func (r *reload) addChain(builder nft.BatchBuilder, chainName string, baseChain nftChain.ChainOptions) error {
|
||||
return builder.Chain().Add(r.table.family, r.table.name, chainName, baseChain)
|
||||
}
|
||||
|
||||
func (r *reload) addRule(builder nft.BatchBuilder, chainName string, rule string) error {
|
||||
return builder.Rule().Add(r.table.family, r.table.name, chainName, rule)
|
||||
}
|
||||
|
||||
func (r *reload) addChainWithReturn(builder nft.BatchBuilder, chainName string, baseChain nftChain.ChainOptions) (chain.Chain, error) {
|
||||
return chain.NewBatchChainWithOptions(builder, r.table.family, r.table.name, chainName, baseChain)
|
||||
}
|
||||
|
||||
func getBlocklistName(blockListName string) string {
|
||||
return "blocklist_" + blockListName
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package firewall
|
||||
|
||||
func (f *firewall) reloadBlockList() error {
|
||||
listBlockedIP, err := f.chains.NewBlockListIP("blocked_ip")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := listBlockedIP.AddRuleToChain(f.chains.BeforeLocalInput().AddRule, "drop"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
listBlockedIPWithPort, err := f.chains.NewBlockListIPWithPort("blocked_ip_port")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := listBlockedIPWithPort.AddRuleToChain(f.chains.BeforeLocalInput().AddRule, "drop"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := f.blockingService.NftReload(listBlockedIP, listBlockedIPWithPort); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
package firewall
|
||||
|
||||
import "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/types"
|
||||
|
||||
func (f *firewall) reloadForward() error {
|
||||
f.logger.Debug("Reloading forward chain")
|
||||
err := f.chains.NewForward(f.config.MetadataNaming.ChainForwardName, f.config.Policy.DefaultAllowForward, f.config.Policy.ForwardPriority)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
chain := f.chains.Forward()
|
||||
|
||||
if err := f.reloadForwardAddIPs(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if f.config.Options.DockerSupport {
|
||||
if err := f.docker.NftChains().ForwardFilterJump(chain.AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if f.config.Policy.DefaultAllowForward == false {
|
||||
drop := f.config.Policy.ForwardDrop.String()
|
||||
if err := chain.AddRule(drop); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *firewall) reloadForwardAddIPs() error {
|
||||
if err := f.chains.NewLocalForward(); err != nil {
|
||||
return err
|
||||
}
|
||||
chain := f.chains.LocalForward()
|
||||
if err := chain.AddRuleIn(f.chains.Forward().AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, ipConfig := range f.config.IP4.InIPs {
|
||||
if ipConfig.Action != types.ActionDrop && ipConfig.Action != types.ActionReject {
|
||||
continue
|
||||
}
|
||||
if err := forwardAddIP(chain.AddRule, ipConfig, "ip"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !f.config.IP6.Enable {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, ipConfig := range f.config.IP6.InIPs {
|
||||
if ipConfig.Action != types.ActionDrop && ipConfig.Action != types.ActionReject {
|
||||
continue
|
||||
}
|
||||
if err := forwardAddIP(chain.AddRule, ipConfig, "ip6"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func forwardAddIP(addRuleFunc func(expr ...string) error, config ConfigIP, ipMatch string) error {
|
||||
rule := ipMatch + " saddr " + config.IP + " iifname != \"lo\""
|
||||
|
||||
// There, during routing, the port changes and then the IP blocking rule will not work.
|
||||
//if !config.OnlyIP {
|
||||
// rule += " " + config.Protocol.String() + " dport " + strconv.Itoa(int(config.Port))
|
||||
//}
|
||||
|
||||
rule += " counter " + config.Action.String()
|
||||
if err := addRuleFunc(rule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,283 +0,0 @@
|
||||
package firewall
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/pkg"
|
||||
)
|
||||
|
||||
func (f *firewall) reloadInput() error {
|
||||
f.logger.Debug("Reloading input chain")
|
||||
err := f.chains.NewInput(f.config.MetadataNaming.ChainInputName, f.config.Policy.DefaultAllowInput, f.config.Policy.InputPriority)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
chain := f.chains.Input()
|
||||
|
||||
if err := f.reloadInputDnsNs(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := chain.AddRule("iifname lo counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := f.chains.NewBeforeLocalInput(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.chains.BeforeLocalInput().AddRuleIn(chain.AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := f.reloadInputAddIPs(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := f.chains.NewAfterLocalInput(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.chains.AfterLocalInput().AddRuleIn(chain.AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := f.chains.PacketFilter().AddRuleIn(chain.AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := f.reloadInputICMP(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := chain.AddRule("iifname != \"lo\" ct state related,established counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := f.reloadInputPorts(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if f.config.Policy.DefaultAllowInput == false {
|
||||
drop := f.config.Policy.InputDrop.String()
|
||||
if err := chain.AddRule("iifname != \"lo\" " + drop); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *firewall) reloadInputDnsNs() error {
|
||||
if f.config.Options.DnsStrictNs {
|
||||
return nil
|
||||
}
|
||||
chain := f.chains.Input()
|
||||
|
||||
addresses, err := pkg.Resolv.Addresses()
|
||||
if err != nil {
|
||||
f.logger.Error(fmt.Sprintf("Failed to get nameservers: %s", err))
|
||||
return nil
|
||||
}
|
||||
for _, addr := range addresses {
|
||||
ip := net.ParseIP(addr)
|
||||
if ip == nil {
|
||||
f.logger.Error(fmt.Sprintf("Failed to parse nameserver address: %s", addr))
|
||||
continue
|
||||
}
|
||||
if ip.To4() != nil {
|
||||
if err := chain.AddRule("ip saddr " + addr + " iifname != \"lo\" tcp dport 53 counter accept"); err != nil {
|
||||
f.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := chain.AddRule("ip saddr " + addr + " iifname != \"lo\" udp dport 53 counter accept"); err != nil {
|
||||
f.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := chain.AddRule("ip saddr " + addr + " iifname != \"lo\" tcp sport 53 counter accept"); err != nil {
|
||||
f.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := chain.AddRule("ip saddr " + addr + " iifname != \"lo\" udp sport 53 counter accept"); err != nil {
|
||||
f.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if ip.To16() != nil {
|
||||
if !f.config.IP6.Enable {
|
||||
f.logger.Warn(fmt.Sprintf("IPv6 is disabled, skipping nameserver address: %s", addr))
|
||||
continue
|
||||
}
|
||||
if err := chain.AddRule("ip6 saddr " + addr + " iifname != \"lo\" tcp dport 53 counter accept"); err != nil {
|
||||
f.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := chain.AddRule("ip6 saddr " + addr + " iifname != \"lo\" udp dport 53 counter accept"); err != nil {
|
||||
f.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := chain.AddRule("ip6 saddr " + addr + " iifname != \"lo\" tcp sport 53 counter accept"); err != nil {
|
||||
f.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := chain.AddRule("ip6 saddr " + addr + " iifname != \"lo\" udp sport 53 counter accept"); err != nil {
|
||||
f.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
f.logger.Error(fmt.Sprintf("Failed to parse nameserver address: %s", addr))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *firewall) reloadInputICMP() error {
|
||||
chain := f.chains.Input()
|
||||
drop := f.config.Policy.InputDrop.String()
|
||||
if f.config.IP4.IcmpIn == false {
|
||||
if err := chain.AddRule("iifname != \"lo\" ip protocol icmp icmp type echo-request counter " + drop); err != nil {
|
||||
return err
|
||||
}
|
||||
return f.reloadInputICMPAfter()
|
||||
}
|
||||
|
||||
if f.config.IP4.IcmpInRate == "0" {
|
||||
return f.reloadInputICMPAfter()
|
||||
}
|
||||
|
||||
if err := chain.AddRule("iifname != \"lo\" ip protocol icmp icmp type echo-request limit rate " + f.config.IP4.IcmpInRate + " counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := chain.AddRule("iifname != \"lo\" ip protocol icmp icmp type echo-request counter " + drop); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return f.reloadInputICMPAfter()
|
||||
}
|
||||
func (f *firewall) reloadInputICMPAfter() error {
|
||||
chain := f.chains.Input()
|
||||
|
||||
if f.config.IP4.IcmpTimestampDrop == true {
|
||||
drop := f.config.Policy.InputDrop.String()
|
||||
if err := chain.AddRule("iifname != \"lo\" ip protocol icmp icmp type timestamp-request " + drop); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := chain.AddRule("iifname != \"lo\" ip protocol icmp counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if f.config.IP6.Enable {
|
||||
if f.config.IP6.IcmpStrict {
|
||||
return f.reloadInputICMP6Strict()
|
||||
} else {
|
||||
if err := chain.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *firewall) reloadInputICMP6Strict() error {
|
||||
chain := f.chains.Input()
|
||||
if err := chain.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type destination-unreachable counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := chain.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type packet-too-big counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := chain.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type time-exceeded counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := chain.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type parameter-problem counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := chain.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type echo-request counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := chain.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type echo-reply counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := chain.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type nd-router-advert ip6 hoplimit 255 counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := chain.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type nd-neighbor-solicit ip6 hoplimit 255 counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := chain.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type nd-neighbor-advert ip6 hoplimit 255 counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := chain.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type nd-redirect ip6 hoplimit 255 counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *firewall) reloadInputPorts() error {
|
||||
chain := f.chains.Input()
|
||||
for _, port := range f.config.InPorts {
|
||||
protocol := port.Port.ProtocolString()
|
||||
number := port.Port.NumberString()
|
||||
|
||||
baseRule := "iifname != \"lo\" meta l4proto " + protocol + " ct state new " + protocol + " dport " + number
|
||||
|
||||
if port.LimitRate != "" {
|
||||
rule := baseRule + " limit rate " + port.LimitRate + " counter " + port.Action.String()
|
||||
if err := chain.AddRule(rule); err != nil {
|
||||
return err
|
||||
}
|
||||
ruleDrop := baseRule + " counter " + f.config.Policy.InputDrop.String()
|
||||
if err := chain.AddRule(ruleDrop); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
rule := baseRule + " counter " + port.Action.String()
|
||||
if err := chain.AddRule(rule); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *firewall) reloadInputAddIPs() error {
|
||||
if err := f.chains.NewLocalInput(); err != nil {
|
||||
return err
|
||||
}
|
||||
chain := f.chains.LocalInput()
|
||||
if err := chain.AddRuleIn(f.chains.Input().AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, ipConfig := range f.config.IP4.InIPs {
|
||||
if err := inputAddIP(chain.AddRule, ipConfig, "ip"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !f.config.IP6.Enable {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, ipConfig := range f.config.IP6.InIPs {
|
||||
if err := inputAddIP(chain.AddRule, ipConfig, "ip6"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func inputAddIP(addRuleFunc func(expr ...string) error, config ConfigIP, ipMatch string) error {
|
||||
|
||||
rule := ipMatch + " saddr " + config.IP + " iifname != \"lo\""
|
||||
if !config.OnlyIP {
|
||||
rule += " " + config.Port.ProtocolString() + " dport " + config.Port.NumberString()
|
||||
}
|
||||
if config.LimitRate != "" {
|
||||
rule += " limit rate " + config.LimitRate
|
||||
}
|
||||
rule += " counter " + config.Action.String()
|
||||
if err := addRuleFunc(rule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,244 +0,0 @@
|
||||
package firewall
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/pkg"
|
||||
)
|
||||
|
||||
func (f *firewall) reloadOutput() error {
|
||||
f.logger.Debug("Reloading output chain")
|
||||
err := f.chains.NewOutput(f.config.MetadataNaming.ChainOutputName, f.config.Policy.DefaultAllowOutput, f.config.Policy.OutputPriority)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
chain := f.chains.Output()
|
||||
|
||||
if err := f.reloadOutputDnsNs(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.reloadOutputDns(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := chain.AddRule("oifname lo counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := f.reloadOutputAddIPs(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := f.chains.PacketFilter().AddRuleOut(chain.AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := f.reloadOutputICMP(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := chain.AddRule("oifname != \"lo\" ct state related,established counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := f.reloadOutputPorts(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if f.config.Policy.DefaultAllowOutput == false {
|
||||
drop := f.config.Policy.OutputDrop.String()
|
||||
if err := chain.AddRule("oifname != \"lo\" " + drop); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *firewall) reloadOutputDns() error {
|
||||
if f.config.Options.DnsStrict {
|
||||
return nil
|
||||
}
|
||||
chain := f.chains.Output()
|
||||
|
||||
if err := chain.AddRule("oifname != \"lo\" tcp dport 53 counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := chain.AddRule("oifname != \"lo\" udp dport 53 counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := chain.AddRule("oifname != \"lo\" tcp sport 53 counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := chain.AddRule("oifname != \"lo\" udp sport 53 counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *firewall) reloadOutputDnsNs() error {
|
||||
if f.config.Options.DnsStrictNs {
|
||||
return nil
|
||||
}
|
||||
chain := f.chains.Output()
|
||||
|
||||
addresses, err := pkg.Resolv.Addresses()
|
||||
if err != nil {
|
||||
f.logger.Error(fmt.Sprintf("Failed to get nameservers: %s", err))
|
||||
return nil
|
||||
}
|
||||
for _, addr := range addresses {
|
||||
ip := net.ParseIP(addr)
|
||||
if ip == nil {
|
||||
f.logger.Error(fmt.Sprintf("Failed to parse nameserver address: %s", addr))
|
||||
continue
|
||||
}
|
||||
if ip.To4() != nil {
|
||||
if err := chain.AddRule("ip daddr " + addr + " oifname != \"lo\" tcp dport 53 counter accept"); err != nil {
|
||||
f.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := chain.AddRule("ip daddr " + addr + " oifname != \"lo\" udp dport 53 counter accept"); err != nil {
|
||||
f.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := chain.AddRule("ip daddr " + addr + " oifname != \"lo\" tcp sport 53 counter accept"); err != nil {
|
||||
f.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := chain.AddRule("ip daddr " + addr + " oifname != \"lo\" udp sport 53 counter accept"); err != nil {
|
||||
f.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if ip.To16() != nil {
|
||||
if err := chain.AddRule("ip6 daddr " + addr + " oifname != \"lo\" tcp dport 53 counter accept"); err != nil {
|
||||
f.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := chain.AddRule("ip6 daddr " + addr + " oifname != \"lo\" udp dport 53 counter accept"); err != nil {
|
||||
f.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := chain.AddRule("ip6 daddr " + addr + " oifname != \"lo\" tcp sport 53 counter accept"); err != nil {
|
||||
f.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := chain.AddRule("ip6 daddr " + addr + " oifname != \"lo\" udp sport 53 counter accept"); err != nil {
|
||||
f.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
f.logger.Error(fmt.Sprintf("Failed to parse nameserver address: %s", addr))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *firewall) reloadOutputICMP() error {
|
||||
chain := f.chains.Output()
|
||||
drop := f.config.Policy.OutputDrop.String()
|
||||
if f.config.IP4.IcmpOut == false {
|
||||
if err := chain.AddRule("oifname != \"lo\" ip protocol icmp icmp type echo-request counter " + drop); err != nil {
|
||||
return err
|
||||
}
|
||||
return f.reloadOutputICMPAfter()
|
||||
}
|
||||
|
||||
if f.config.IP4.IcmpOutRate == "0" {
|
||||
return f.reloadOutputICMPAfter()
|
||||
}
|
||||
|
||||
if err := chain.AddRule("oifname != \"lo\" ip protocol icmp icmp type echo-request limit rate " + f.config.IP4.IcmpInRate + " counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := chain.AddRule("oifname != \"lo\" ip protocol icmp icmp type echo-request counter " + drop); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return f.reloadOutputICMPAfter()
|
||||
}
|
||||
|
||||
func (f *firewall) reloadOutputICMPAfter() error {
|
||||
chain := f.chains.Output()
|
||||
|
||||
if f.config.IP4.IcmpTimestampDrop == true {
|
||||
drop := f.config.Policy.OutputDrop.String()
|
||||
if err := chain.AddRule("oifname != \"lo\" ip protocol icmp icmp type timestamp-request " + drop); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := chain.AddRule("oifname != \"lo\" ip protocol icmp counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *firewall) reloadOutputPorts() error {
|
||||
chain := f.chains.Output()
|
||||
for _, port := range f.config.OutPorts {
|
||||
protocol := port.Port.ProtocolString()
|
||||
number := port.Port.NumberString()
|
||||
baseRule := "oifname != \"lo\" meta l4proto " + protocol + " ct state new " + protocol + " dport " + number
|
||||
|
||||
if port.LimitRate != "" {
|
||||
rule := baseRule + " limit rate " + port.LimitRate + " counter " + port.Action.String()
|
||||
if err := chain.AddRule(rule); err != nil {
|
||||
return err
|
||||
}
|
||||
ruleDrop := baseRule + " counter " + f.config.Policy.InputDrop.String()
|
||||
if err := chain.AddRule(ruleDrop); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
rule := baseRule + " counter " + port.Action.String()
|
||||
if err := chain.AddRule(rule); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *firewall) reloadOutputAddIPs() error {
|
||||
if err := f.chains.NewLocalOutput(); err != nil {
|
||||
return err
|
||||
}
|
||||
chain := f.chains.LocalOutput()
|
||||
if err := chain.AddRuleOut(f.chains.Output().AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, ipConfig := range f.config.IP4.OutIPs {
|
||||
if err := outputAddIP(chain.AddRule, ipConfig, "ip"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !f.config.IP6.Enable {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, ipConfig := range f.config.IP6.OutIPs {
|
||||
if err := outputAddIP(chain.AddRule, ipConfig, "ip6"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func outputAddIP(addRuleFunc func(expr ...string) error, config ConfigIP, ipMatch string) error {
|
||||
|
||||
rule := ipMatch + " daddr " + config.IP + " oifname != \"lo\""
|
||||
if !config.OnlyIP {
|
||||
rule += " " + config.Port.ProtocolString() + " dport " + config.Port.NumberString()
|
||||
}
|
||||
if config.LimitRate != "" {
|
||||
rule += " limit rate " + config.LimitRate
|
||||
}
|
||||
rule += " counter " + config.Action.String()
|
||||
if err := addRuleFunc(rule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -41,6 +41,30 @@ func (a Action) String() string {
|
||||
}
|
||||
}
|
||||
|
||||
type KnockAction int8
|
||||
|
||||
const (
|
||||
KnockActionAccept KnockAction = iota + 1
|
||||
KnockActionReject
|
||||
KnockActionDrop
|
||||
KnockActionReturn
|
||||
)
|
||||
|
||||
func (a KnockAction) String() string {
|
||||
switch a {
|
||||
case KnockActionAccept:
|
||||
return "accept"
|
||||
case KnockActionReject:
|
||||
return "reject"
|
||||
case KnockActionDrop:
|
||||
return "drop"
|
||||
case KnockActionReturn:
|
||||
return "return"
|
||||
default:
|
||||
return "drop"
|
||||
}
|
||||
}
|
||||
|
||||
type Protocol int8
|
||||
|
||||
const (
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package geoip
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/geoip2"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
GeoIP geoip2.RefreshableGeoIP2
|
||||
Interval time.Duration
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package geoip
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type falseGeoIP struct {
|
||||
}
|
||||
|
||||
func NewFalseGeoIP() GeoIP {
|
||||
return &falseGeoIP{}
|
||||
}
|
||||
|
||||
func (g *falseGeoIP) Info(ip string) (string, error) {
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
func (g *falseGeoIP) Run(_ context.Context) {
|
||||
|
||||
}
|
||||
|
||||
func (g *falseGeoIP) Refresh(_ context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *falseGeoIP) Close() error {
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package geoip
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/geoip2"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
)
|
||||
|
||||
type Info func(ip string) (string, error)
|
||||
|
||||
type GeoIP interface {
|
||||
Info(ip string) (string, error)
|
||||
Run(ctx context.Context)
|
||||
Refresh(ctx context.Context) (err error)
|
||||
Close() error
|
||||
}
|
||||
|
||||
type geoIP struct {
|
||||
config *Config
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
func New(config *Config, logger log.Logger) GeoIP {
|
||||
return &geoIP{
|
||||
config: config,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (g *geoIP) Info(ip string) (string, error) {
|
||||
if g.config.GeoIP == nil {
|
||||
return ip, fmt.Errorf("geoip is not initialized")
|
||||
}
|
||||
|
||||
addr, err := netip.ParseAddr(ip)
|
||||
if err != nil {
|
||||
return ip, err
|
||||
}
|
||||
|
||||
info, err := g.config.GeoIP.Info(addr)
|
||||
if err != nil {
|
||||
if errors.Is(err, geoip2.ErrNotFound) {
|
||||
g.logger.Warn(fmt.Sprintf("failed to get geoip info for ip %s: %v", ip, err))
|
||||
return ip, nil
|
||||
}
|
||||
return ip, err
|
||||
}
|
||||
g.logger.Debug(fmt.Sprintf("geoip info for ip %s: %s", ip, info.ToString()))
|
||||
|
||||
return info.ToString(), nil
|
||||
}
|
||||
|
||||
func (g *geoIP) Run(ctx context.Context) {
|
||||
g.logger.Debug("geoip service started")
|
||||
go func() {
|
||||
ticker := time.NewTicker(g.config.Interval)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
g.logger.Debug("refreshing geoip data")
|
||||
if err := g.config.GeoIP.Refresh(ctx); err != nil {
|
||||
g.logger.Error(fmt.Sprintf("failed to refresh geoip data: %v", err))
|
||||
}
|
||||
g.logger.Debug("geoip data refreshed")
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (g *geoIP) Refresh(ctx context.Context) (err error) {
|
||||
return g.config.GeoIP.Refresh(ctx)
|
||||
}
|
||||
|
||||
func (g *geoIP) Close() error {
|
||||
g.logger.Debug("geoip service stopped")
|
||||
return g.config.GeoIP.Close()
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package geoip
|
||||
|
||||
import (
|
||||
"git.kor-elf.net/kor-elf-shield/geoip2"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
)
|
||||
|
||||
type logger struct {
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
func NewLogger(log log.Logger) geoip2.Logger {
|
||||
return &logger{logger: log}
|
||||
}
|
||||
|
||||
func (l *logger) Error(err error) {
|
||||
l.logger.Error(err.Error())
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package info
|
||||
|
||||
import (
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db/entity"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db/repository"
|
||||
)
|
||||
|
||||
func NewMetadataFirewallFileNft(repo repository.MetadataRepository) Metadata {
|
||||
return NewMetadata(repo, entity.MetadataKeyFirewallFileNft)
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package info
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type Info interface {
|
||||
// Version returns the version of the daemon.
|
||||
Version() string
|
||||
|
||||
// IsVersionChanged returns true if the version of the daemon has changed.
|
||||
IsVersionChanged() bool
|
||||
|
||||
// IsSettingsChanged returns true if the settings of the daemon has changed.
|
||||
IsSettingsChanged() bool
|
||||
|
||||
// BuiltWith returns the build information of the daemon.
|
||||
BuiltWith() string
|
||||
// StartTime returns the start time of the daemon.
|
||||
StartTime() time.Time
|
||||
// Uptime returns the uptime of the daemon.
|
||||
Uptime() time.Duration
|
||||
|
||||
// Metadata returns the metadata of the daemon.
|
||||
Metadata() MetadataContainer
|
||||
}
|
||||
|
||||
type info struct {
|
||||
version string
|
||||
isVersionChanged bool
|
||||
|
||||
isSettingsChanged bool
|
||||
|
||||
builtWith string
|
||||
startTime time.Time
|
||||
|
||||
metadata MetadataContainer
|
||||
}
|
||||
|
||||
func New(
|
||||
version string,
|
||||
isVersionChanged bool,
|
||||
|
||||
builtWith string,
|
||||
startTime time.Time,
|
||||
|
||||
isSettingsChanged bool,
|
||||
|
||||
metadata MetadataContainer,
|
||||
) Info {
|
||||
return &info{
|
||||
version: version,
|
||||
isVersionChanged: isVersionChanged,
|
||||
|
||||
isSettingsChanged: isSettingsChanged,
|
||||
|
||||
builtWith: builtWith,
|
||||
startTime: startTime,
|
||||
metadata: metadata,
|
||||
}
|
||||
}
|
||||
|
||||
func (i *info) Version() string {
|
||||
return i.version
|
||||
}
|
||||
|
||||
func (i *info) IsVersionChanged() bool {
|
||||
return i.isVersionChanged
|
||||
}
|
||||
|
||||
func (i *info) IsSettingsChanged() bool {
|
||||
return i.isSettingsChanged
|
||||
}
|
||||
|
||||
func (i *info) BuiltWith() string {
|
||||
return i.builtWith
|
||||
}
|
||||
|
||||
func (i *info) StartTime() time.Time {
|
||||
return i.startTime
|
||||
}
|
||||
|
||||
func (i *info) Uptime() time.Duration {
|
||||
return time.Since(i.StartTime())
|
||||
}
|
||||
|
||||
func (i *info) Metadata() MetadataContainer {
|
||||
return i.metadata
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package info
|
||||
|
||||
import (
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db/entity"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db/repository"
|
||||
)
|
||||
|
||||
type MetadataContainer interface {
|
||||
FirewallFileNft() Metadata
|
||||
}
|
||||
|
||||
type Metadata interface {
|
||||
Get() (string, error)
|
||||
Update(data string) error
|
||||
}
|
||||
|
||||
func NewMetadataContainer(
|
||||
firewallFileNft Metadata,
|
||||
) MetadataContainer {
|
||||
return &metadataContainer{
|
||||
firewallFileNft: firewallFileNft,
|
||||
}
|
||||
}
|
||||
|
||||
func NewMetadata(repo repository.MetadataRepository, key string) Metadata {
|
||||
return &metadata{
|
||||
key: key,
|
||||
repo: repo,
|
||||
}
|
||||
}
|
||||
|
||||
type metadataContainer struct {
|
||||
firewallFileNft Metadata
|
||||
}
|
||||
|
||||
func (m *metadataContainer) FirewallFileNft() Metadata {
|
||||
return m.firewallFileNft
|
||||
}
|
||||
|
||||
type metadata struct {
|
||||
key string
|
||||
repo repository.MetadataRepository
|
||||
}
|
||||
|
||||
func (m *metadata) Get() (string, error) {
|
||||
meta, err := m.repo.Get(m.key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return meta.Value, nil
|
||||
}
|
||||
|
||||
func (m *metadata) Update(value string) error {
|
||||
meta := &entity.Metadata{Value: value}
|
||||
return m.repo.Update(m.key, meta)
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package info
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db/entity"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db/repository"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
)
|
||||
|
||||
func IsSettingsChanged(repo repository.MetadataRepository, listPathFiles map[string]string, logger log.Logger) bool {
|
||||
isChanged := false
|
||||
|
||||
for k, f := range listPathFiles {
|
||||
hash, err := fileHash(f)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("Failed to get hash of %s: %s", f, err))
|
||||
continue
|
||||
}
|
||||
|
||||
settingMetadata := NewMetadata(repo, entity.KeySetting(k))
|
||||
metadataValue, err := settingMetadata.Get()
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("Failed to get metadata value: %s", err))
|
||||
}
|
||||
|
||||
if metadataValue != hash {
|
||||
isChanged = true
|
||||
if err := settingMetadata.Update(hash); err != nil {
|
||||
logger.Error(fmt.Sprintf("Failed to update metadata: %s", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isChanged
|
||||
}
|
||||
|
||||
func fileHash(path string) (string, error) {
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
size := info.Size()
|
||||
modTime := info.ModTime()
|
||||
|
||||
return fmt.Sprintf("%d-%d", size, modTime.Unix()), nil
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package info
|
||||
|
||||
import (
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db/entity"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db/repository"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
)
|
||||
|
||||
func IsVersionChanged(repo repository.MetadataRepository, version string, logger log.Logger) bool {
|
||||
metadata := NewMetadata(repo, entity.MetadataKeyVersion)
|
||||
if v, err := metadata.Get(); err != nil {
|
||||
logger.Error(err.Error())
|
||||
return false
|
||||
} else if v != version {
|
||||
if err := metadata.Update(version); err != nil {
|
||||
logger.Error(err.Error())
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/analyzer/config"
|
||||
analyzerConfig "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/analyzer/config"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall"
|
||||
firewallConfig "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/config"
|
||||
)
|
||||
|
||||
type DaemonOptions struct {
|
||||
@@ -11,7 +11,7 @@ type DaemonOptions struct {
|
||||
PathSocketFile string
|
||||
DataDir string
|
||||
PathNftables string
|
||||
ConfigFirewall firewall.Config
|
||||
ConfigAnalyzer config.Config
|
||||
ConfigFirewall firewallConfig.Config
|
||||
ConfigAnalyzer analyzerConfig.Config
|
||||
Repositories db.Repositories
|
||||
}
|
||||
|
||||
@@ -2,19 +2,31 @@ package daemon
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/analyzer"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/analyzer/log/analysis/brute_force_protection_group"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/blocklist"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor"
|
||||
firewall2 "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/blocking"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/geoip"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/info"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/notifications"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/pidfile"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/socket"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
)
|
||||
|
||||
func NewDaemon(opts DaemonOptions, logger log.Logger, notifications notifications.Notifications, docker docker_monitor.Docker) (Daemon, error) {
|
||||
func NewDaemon(
|
||||
info info.Info,
|
||||
opts DaemonOptions,
|
||||
logger log.Logger,
|
||||
notifications notifications.Notifications,
|
||||
docker docker_monitor.Docker,
|
||||
blocklist blocklist.Blocklist,
|
||||
geoIPService geoip.GeoIP,
|
||||
) (Daemon, error) {
|
||||
if logger == nil {
|
||||
return nil, errors.New("logger is nil")
|
||||
}
|
||||
@@ -30,12 +42,27 @@ func NewDaemon(opts DaemonOptions, logger log.Logger, notifications notification
|
||||
}
|
||||
|
||||
blockingService := blocking.New(opts.Repositories.Blocking(), logger)
|
||||
firewall, err := firewall2.New(opts.PathNftables, blockingService, logger, opts.ConfigFirewall, docker)
|
||||
|
||||
dataDirForFirewall := strings.TrimRight(opts.DataDir, "/") + "/firewall"
|
||||
|
||||
firewall, err := firewall2.New(
|
||||
opts.PathNftables,
|
||||
blockingService,
|
||||
logger,
|
||||
opts.ConfigFirewall,
|
||||
docker,
|
||||
blocklist,
|
||||
dataDirForFirewall,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blockService := brute_force_protection_group.NewBlockService(firewall.BlockIP, firewall.BlockIPWithPorts)
|
||||
analyzerService := analyzer.New(opts.ConfigAnalyzer, blockService, opts.Repositories, logger, notifications)
|
||||
analyzerService := analyzer.New(opts.ConfigAnalyzer, blockService, opts.Repositories, logger, notifications, geoIPService.Info)
|
||||
|
||||
return &daemon{
|
||||
info: info,
|
||||
pidFile: pidFile,
|
||||
socket: sock,
|
||||
logger: logger,
|
||||
@@ -43,5 +70,7 @@ func NewDaemon(opts DaemonOptions, logger log.Logger, notifications notification
|
||||
notifications: notifications,
|
||||
analyzer: analyzerService,
|
||||
docker: docker,
|
||||
blocklist: blocklist,
|
||||
geoIPService: geoIPService,
|
||||
}, nil
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user