34 Commits

Author SHA1 Message Date
kor-elf 72a0f941a1 Merge pull request 'v0.11.0' (#11) from develop into main
Reviewed-on: #11
2026-05-07 17:03:00 +05:00
kor-elf e9cd163ae7 Update: set release date for version 0.11.0 in CHANGELOG 2026-05-07 16:31:49 +05:00
kor-elf f778e25575 Update: improve notification blocking time display in CHANGELOG 2026-05-05 20:42:40 +05:00
kor-elf 21ab1802fe Update: improve blockSec message formatting with human-readable duration and adjust locale translations 2026-05-05 20:41:08 +05:00
kor-elf 62517e6f4c Update: add options.cache parameter to firewall.toml for nftables caching and improve CHANGELOG formatting 2026-05-05 20:31:20 +05:00
kor-elf 8a7608dac6 Refactor: integrate info package to manage daemon metadata and versioning, streamline dependencies, and enhance firewall reload logic 2026-05-05 20:28:51 +05:00
kor-elf 8d90a3770d Update: add cache support to firewall settings for improved nftables command handling 2026-05-05 20:27:25 +05:00
kor-elf 4acb81b5a7 Update: extend settings functionality with path-based configuration and file listing 2026-05-05 20:26:41 +05:00
kor-elf 49ab9c48c7 Refactor: remove unused Metadata implementation from firewall package 2026-05-05 20:23:08 +05:00
kor-elf 28019ec171 Update: introduce info package to manage daemon metadata, versioning, settings tracking, and uptime calculation 2026-05-05 20:22:49 +05:00
kor-elf 03b4009f96 Update: add constants for metadata keys and refactor Metadata struct 2026-05-05 20:19:02 +05:00
kor-elf 71502ff0c9 Refactor: ensure file handle is properly closed in checksum calculation logic 2026-05-05 20:17:13 +05:00
kor-elf ab59b356dc Update: add metadata management and caching support for nftables reload logic 2026-05-04 23:50:13 +05:00
kor-elf 8595c6791d Update: rework IP blocklist update logic and improve Uptime output in kor-elf-shield status 2026-05-04 20:38:51 +05:00
kor-elf c64c94dceb Refactor: use HumanDuration for uptime formatting and add utility to format durations elegantly 2026-05-04 20:38:35 +05:00
kor-elf a387e85569 Refactor: enhance blocklist management with file-based element replacements, checksum validation, and modular NFTables integration 2026-05-04 20:38:12 +05:00
kor-elf ab8466ada2 Refactor: rename loop variable in docker rule reload for improved clarity 2026-05-04 20:37:29 +05:00
kor-elf d978343f4c Refactor: improve readability in firewall rule addition by renaming variables for clarity and simplifying conditional logic 2026-05-04 20:36:46 +05:00
kor-elf 14a6b9df0b Update: rework nftables rule addition logic to use temporary files and -f execution 2026-05-03 22:39:02 +05:00
kor-elf 298c7140a4 Refactor: remove unused chain-related implementations from the firewall package 2026-05-03 22:30:54 +05:00
kor-elf 527b6c8264 Refactor: modularize firewall initialization with data directory support and enhance nftables reload logic 2026-05-03 22:30:38 +05:00
kor-elf 0d707ac3c6 Refactor: add Clear method to NFT table and update initialization to support table attributes 2026-05-03 22:30:09 +05:00
kor-elf b535195c1f Refactor: enhance blocklist management with modular NFTables blocklist implementation, adding support for rule reloading and element replacements 2026-05-03 21:50:33 +05:00
kor-elf 3c12429f0e Refactor: simplify RunBatch usage in firewall blocking implementation 2026-05-03 17:19:15 +05:00
kor-elf 25ee39c0ec Refactor: add IP and IP-with-port block list management to firewall with batch support 2026-05-03 17:05:46 +05:00
kor-elf 95e1f274f7 Refactor: remove unused chain package from Docker monitor implementation 2026-05-02 23:25:33 +05:00
kor-elf 5f72efd1bf Refactor: remove unused chainCommand method and chain import from Docker monitor 2026-05-02 23:25:13 +05:00
kor-elf e8826cb86b Refactor: integrate NFTDocker abstraction in rule strategies and update chain handling methods 2026-05-02 23:20:59 +05:00
kor-elf f12097b280 Implemented reloading of nftables rules via batch 2026-05-02 23:19:26 +05:00
kor-elf 3c040945bc Upgrade go-nftables-client to v0.2.1 and update CHANGELOG accordingly 2026-04-27 23:46:03 +05:00
kor-elf 0fdc07c0af Fix: handle error from service initialization in daemon server setup 2026-04-27 22:38:37 +05:00
kor-elf a1345bd3e1 Refactor: move firewall-related configurations to new config package and update references 2026-04-27 22:07:02 +05:00
kor-elf d2f3640b75 Update CHANGELOG for upcoming v0.11.0 release with go-nftables-client update details 2026-04-26 17:41:08 +05:00
kor-elf 1363ff4bef Upgrade go-nftables-client to v0.2.0 and update imports to reflect new contract package structure 2026-04-26 17:36:47 +05:00
94 changed files with 3088 additions and 2388 deletions
+27
View File
@@ -1,3 +1,30 @@
## 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-адресах (континент, страна, город, часовой пояс).
+11
View File
@@ -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" если вы уверены в своих действиях.
+1 -1
View File
@@ -5,7 +5,7 @@ go 1.25
require (
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.1.1
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
+2 -2
View File
@@ -2,8 +2,8 @@ git.kor-elf.net/kor-elf-shield/blocklist v1.1.0 h1:NS8be3TFBsUn+ft3oG5sAD56iJTGO
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.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=
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=
+23 -6
View File
@@ -3,6 +3,7 @@ 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"
@@ -10,6 +11,7 @@ import (
"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"
@@ -83,12 +85,9 @@ func runDaemon(ctx context.Context, _ *cli.Command) error {
_ = geoIPService.Close()
}()
info := daemon.DaemonInfo{
Ver: setting.AppVer,
BuiltWith: setting.AppBuiltWith,
StartTime: setting.AppStartTime,
}
d, err := daemon.NewDaemon(info, config, logger, notificationsService, dockerService, blocklistService, geoIPService)
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())
@@ -109,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 {
@@ -150,6 +166,7 @@ func newBlocklistService(ctx context.Context, blocklistRepository repository.Blo
blocklistConfig := blocklist.Config{
BlocklistRepository: blocklistRepository,
Sources: config,
PathDir: strings.TrimRight(setting.Config.DataDir, "/") + "/blocklists",
}
blocklistService, err := blocklist.New(blocklistConfig, ctx, logger)
@@ -14,6 +14,7 @@ import (
"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 {
@@ -301,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,
+103 -45
View File
@@ -3,19 +3,20 @@ 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/chain/block"
"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 newBlocklist func(name string) (block.Blocklist, error)
type Blocklist interface {
NftReload(newBlocklist newBlocklist) error
Names() []string
NftReload(blocks map[string]block.Blocklist) error
Run()
Close() error
}
@@ -26,7 +27,8 @@ type updateSource struct {
}
type blocklist struct {
Sources []*SourceConfig
pathDir string
sources []*SourceConfig
blocklistRepository repository.BlocklistRepository
logger log.Logger
@@ -41,8 +43,17 @@ type blocklist struct {
}
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{
Sources: config.Sources,
pathDir: config.PathDir,
sources: config.Sources,
blocklistRepository: config.BlocklistRepository,
logger: logger,
ctx: ctx,
@@ -54,34 +65,41 @@ func New(config Config, ctx context.Context, logger log.Logger) (Blocklist, erro
}, nil
}
func (b *blocklist) NftReload(newBlocklist newBlocklist) error {
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")
for _, source := range b.Sources {
b.logger.Debug(fmt.Sprintf("Reload blocklist from %s", source.Name))
if source.Name == "" {
continue
}
nftBlocklist, err := newBlocklist("blocklist_" + source.Name)
if err != nil {
b.logger.Error(fmt.Sprintf("Failed to create blocklist: %s", err))
continue
}
b.mu.Lock()
b.nftBlocklists = blocks
b.mu.Unlock()
b.mu.Lock()
b.nftBlocklists[source.Name] = nftBlocklist
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 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 listEntity.IsFresh(source.Interval) {
if err := nftBlocklist.ReplaceElementsIPv4(listEntity.IPsV4); len(listEntity.IPsV4) > 0 && err != nil {
b.logger.Error(fmt.Sprintf("Failed to replace elements (IPv4): %s", err))
}
if err := nftBlocklist.ReplaceElementsIPv6(listEntity.IPsV6); len(listEntity.IPsV6) > 0 && err != nil {
b.logger.Error(fmt.Sprintf("Failed to replace elements (IPv6): %s", err))
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))
}
}
@@ -98,7 +116,7 @@ func (b *blocklist) Run() {
b.ctx, b.cancel = context.WithCancel(b.ctx)
go b.processUpdateData(b.ctx)
for _, src := range b.Sources {
for _, src := range b.sources {
if src == nil || src.Name == "" {
continue
}
@@ -158,7 +176,7 @@ func (b *blocklist) processUpdateData(ctx context.Context) {
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 listEntity.IsFresh(updSource.source.Interval) {
} else if b.isFresh(updSource.source, listEntity) {
b.logger.Debug(fmt.Sprintf("blocklist %s is fresh", updSource.source.Name))
continue
}
@@ -178,24 +196,24 @@ func (b *blocklist) refreshSource(sourceConfig *SourceConfig) {
if nftBlocklist, ok := b.nftBlocklists[sourceConfig.Name]; ok {
listEntity := &entity.Blocklist{
UpdatedAtUnix: time.Now().Unix(),
IPsV4: nil,
IPsV6: nil,
}
if len(ipsV4) > 0 {
if err := nftBlocklist.ReplaceElementsIPv4(ipsV4); err != nil {
b.logger.Error(fmt.Sprintf("Failed to replace elements (IPv4): %s", err))
} else {
listEntity.IPsV4 = ipsV4
}
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 len(ipsV6) > 0 {
if err := nftBlocklist.ReplaceElementsIPv6(ipsV6); err != nil {
b.logger.Error(fmt.Sprintf("Failed to replace elements (IPv6): %s", err))
} else {
listEntity.IPsV6 = ipsV6
}
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 {
@@ -220,3 +238,43 @@ func (b *blocklist) Close() error {
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
}
+1
View File
@@ -10,6 +10,7 @@ import (
type Config struct {
BlocklistRepository repository.BlocklistRepository
Sources []*SourceConfig
PathDir string
}
type SourceConfig struct {
+7 -1
View File
@@ -1,5 +1,7 @@
package blocklist
import "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/block"
type FalseBlocklist struct {
}
@@ -7,7 +9,11 @@ func NewFalseBlocklist() Blocklist {
return &FalseBlocklist{}
}
func (b *FalseBlocklist) NftReload(_ newBlocklist) error {
func (b *FalseBlocklist) Names() []string {
return []string{}
}
func (b *FalseBlocklist) NftReload(_ map[string]block.Blocklist) error {
return nil
}
+6 -12
View File
@@ -17,6 +17,7 @@ import (
"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"
@@ -32,7 +33,7 @@ type Daemon interface {
}
type daemon struct {
info DaemonInfo
info info.Info
pidFile pidfile.PidFile
socket socket.Socket
logger log.Logger
@@ -46,12 +47,6 @@ type daemon struct {
stopCh chan struct{}
}
type DaemonInfo struct {
Ver string
BuiltWith string
StartTime time.Time
}
func (d *daemon) Run(ctx context.Context, isTesting bool, testingInterval uint16) error {
if err := d.pidFile.EnsureNoOtherProcess(); err != nil {
return err
@@ -59,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
}
@@ -164,7 +159,6 @@ func (d *daemon) socketCommand(command string, args map[string]string, socket so
return socket.Write("ok")
case "status":
uptime := time.Since(d.info.StartTime)
var m runtime.MemStats
runtime.ReadMemStats(&m)
@@ -180,9 +174,9 @@ func (d *daemon) socketCommand(command string, args map[string]string, socket so
"HeapSys: %s\n"+
"NumGC: %d\n"+
"***\n",
d.info.Ver,
d.info.BuiltWith,
uptime,
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.
+7
View File
@@ -20,6 +20,7 @@ type Repositories interface {
BruteForceProtectionGroup() repository.BruteForceProtectionGroupRepository
Blocking() repository.BlockingRepository
Blocklist() repository.BlocklistRepository
Metadata() repository.MetadataRepository
Close() error
}
@@ -30,6 +31,7 @@ type repositories struct {
bruteForceProtectionGroup repository.BruteForceProtectionGroupRepository
blocking repository.BlockingRepository
blocklist repository.BlocklistRepository
metadata repository.MetadataRepository
db []*bbolt.DB
}
@@ -60,6 +62,7 @@ func New(dataDir string) (Repositories, error) {
bruteForceProtectionGroup: repository.NewBruteForceProtectionGroupRepository(securityDB),
blocking: repository.NewBlockingRepository(securityDB),
blocklist: repository.NewBlocklistRepository(securityDB),
metadata: repository.NewMetadataRepository(appDB),
db: []*bbolt.DB{appDB, securityDB},
}, nil
@@ -85,6 +88,10 @@ 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()
+6 -3
View File
@@ -5,13 +5,16 @@ import (
)
type Blocklist struct {
UpdatedAtUnix int64 `json:"UpdateAtUnix"`
IPsV4 []string `json:"IPsV4"`
IPsV6 []string `json:"IPsV6"`
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
}
+14
View File
@@ -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
}
+65
View File
@@ -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)
})
}
@@ -13,6 +13,7 @@ const (
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
}
+4 -16
View File
@@ -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())
}
}
+22 -7
View File
@@ -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,65 +0,0 @@
package block
import (
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
)
type Blocklist interface {
// ReplaceElementsIPv4 Replacing IP addresses.
ReplaceElementsIPv4(ips []string) error
// ReplaceElementsIPv6 Replacing IP addresses.
ReplaceElementsIPv6(ips []string) error
// AddRuleToChain Add a rule to the parent chain.
AddRuleToChain(chainAddRuleFunc func(expr ...string) error, action string) error
}
type blocklist struct {
listIPv4 List
listIPv6 List
}
func NewBlocklist(nft nft.NFT, family family.Type, table string, name string) (Blocklist, error) {
params := "type ipv4_addr; flags interval; auto-merge;"
listName := name + "_ip4"
listIPv4, err := newList(nft, family, table, listName, params)
if err != nil {
return nil, err
}
params = "type ipv6_addr; flags interval; auto-merge;"
listName = name + "_ip6"
listIPv6, err := newList(nft, family, table, listName, params)
if err != nil {
return nil, err
}
return &blocklist{
listIPv4: listIPv4,
listIPv6: listIPv6,
}, nil
}
func (l *blocklist) ReplaceElementsIPv4(ips []string) error {
return l.listIPv4.ReplaceElements(ips)
}
func (l *blocklist) ReplaceElementsIPv6(ips []string) error {
return l.listIPv6.ReplaceElements(ips)
}
func (l *blocklist) 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,101 +0,0 @@
package block
import (
"fmt"
"strings"
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
ReplaceElements(elements []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...)
}
func (l *list) ReplaceElements(elements []string) error {
if len(elements) == 0 {
return nil
}
if err := l.nft.Command().Run("flush set", l.family.String(), l.table, l.name); err != nil {
return err
}
const batchSize = 200
for _, batch := range chunkStrings(elements, batchSize) {
command := []string{
"add element",
l.family.String(), l.table, l.name,
fmt.Sprintf("{ %s }", strings.Join(batch, ",")),
}
if err := l.nft.Command().Run(command...); err != nil {
return err
}
}
return nil
}
func chunkStrings(items []string, size int) [][]string {
if size <= 0 {
size = 100
}
chunks := make([][]string, 0, (len(items)+size-1)/size)
for start := 0; start < len(items); start += size {
end := start + size
if end > len(items) {
end = len(items)
}
chunks = append(chunks, items[start:end])
}
return chunks
}
@@ -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,20 +0,0 @@
package block
import (
"strconv"
"strings"
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/pkg/ip"
)
func NewPortKnocking(nft nft.NFT, family family.Type, table string, name string, ipVersion ip.Version, timeout uint32) error {
params := []string{"type", ipVersion.ToNftForSet() + ";", "flags timeout; timeout", strconv.Itoa(int(timeout)) + "s;"}
_, err := newList(nft, family, table, name, strings.Join(params, " "))
if err != nil {
return err
}
return nil
}
-67
View File
@@ -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)
}
-276
View File
@@ -1,276 +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)
NewBlocklist(name string) (block.Blocklist, error)
NewPortKnocking(name string) (PortKnocking, 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 (c *chains) NewBlocklist(name string) (block.Blocklist, error) {
blockList, err := block.NewBlocklist(c.nft, c.family, c.table, name)
if err != nil {
return nil, err
}
if err := blockList.AddRuleToChain(c.afterLocalInput.AddRule, "drop"); err != nil {
return nil, err
}
return blockList, nil
}
func (c *chains) NewPortKnocking(name string) (PortKnocking, error) {
portKnocking, err := newPortKnocking(c.nft, c.family, c.table, name)
if err != nil {
return nil, err
}
return portKnocking, 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
}
-48
View File
@@ -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...)
}
-48
View File
@@ -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)
}
-48
View File
@@ -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,4 +1,4 @@
package firewall
package config
import (
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/types"
@@ -17,6 +17,7 @@ type Config struct {
}
type ConfigOptions struct {
Cache bool
ClearMode ClearMode
SavesRules bool
SavesRulesPath string
-72
View File
@@ -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
}
+115 -46
View File
@@ -4,19 +4,26 @@ import (
"fmt"
"net"
"os"
"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/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()
@@ -44,79 +51,93 @@ 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,
config config.Config,
docker docker_monitor.Docker,
blocklist blocklist.Blocklist,
dataDir string,
) (API, error) {
nft, err := nftables.NewWithPath(pathNFT)
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(f.chains.NewBlocklist); err != nil {
if err := f.blocklist.NftReload(nftTable.BlockList().Blocks()); err != nil {
f.logger.Error(fmt.Sprintf("Failed to reload blocklist: %s", err))
}
@@ -128,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
@@ -167,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
@@ -204,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
}
+120
View File
@@ -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
}
@@ -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
}
@@ -2,11 +2,12 @@ package chain
import (
"strconv"
"strings"
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
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/chain/block"
"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"
)
@@ -26,31 +27,28 @@ type PortKnocking interface {
timeout uint32,
action types.KnockAction,
) error
AddRuleIn(AddRuleFunc func(expr ...string) error) error
AddRuleIn(AddRuleFunc rule.AddFunc) error
}
type portKnocking struct {
nft nft.NFT
family family.Type
table string
chain string
chain Chain
sets block.Sets
}
func newPortKnocking(nft nft.NFT, family family.Type, table string, chain string) (PortKnocking, error) {
if err := nft.Chain().Add(family, table, chain, nftChain.TypeNone); err != nil {
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{
nft: nft,
family: family,
table: table,
chain: chain,
chain: batchChain,
sets: block.NewBatchSet(builder, family, table),
}, nil
}
func (k *portKnocking) AddRuleIn(AddRuleFunc func(expr ...string) error) error {
return AddRuleFunc("iifname != \"lo\" counter jump " + k.chain)
func (k *portKnocking) AddRuleIn(AddRuleFunc rule.AddFunc) error {
return k.chain.AddRuleIn(AddRuleFunc)
}
func (k *portKnocking) AddFirstStageRule(
@@ -60,7 +58,7 @@ func (k *portKnocking) AddFirstStageRule(
timeout uint32,
action types.KnockAction,
) error {
if err := block.NewPortKnocking(k.nft, k.family, k.table, name, ipVersion, timeout); err != nil {
if err := k.newPortKnocking(name, ipVersion, timeout); err != nil {
return err
}
@@ -68,7 +66,7 @@ func (k *portKnocking) AddFirstStageRule(
l4Port.ProtocolString(), "dport", l4Port.NumberString(), "add", "@" + name,
"{", ipVersion.ToNft(), "saddr timeout", strconv.Itoa(int(timeout)) + "s", "}", action.String(),
}
return k.nft.Rule().Add(k.family, k.table, k.chain, expr...)
return k.chain.AddRule(expr...)
}
func (k *portKnocking) AddNextStageRule(
@@ -78,7 +76,7 @@ func (k *portKnocking) AddNextStageRule(
timeout uint32,
action types.KnockAction,
) error {
if err := block.NewPortKnocking(k.nft, k.family, k.table, nextName, ipVersion, timeout); err != nil {
if err := k.newPortKnocking(nextName, ipVersion, timeout); err != nil {
return err
}
@@ -87,5 +85,10 @@ func (k *portKnocking) AddNextStageRule(
l4Port.ProtocolString(), "dport", l4Port.NumberString(), "add", "@" + nextName,
"{", ipVersion.ToNft(), "saddr}", action.String(),
}
return k.nft.Rule().Add(k.family, k.table, k.chain, expr...)
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, " "))
}
+13
View File
@@ -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"`
}
+54
View File
@@ -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)
}
+45
View File
@@ -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
}
+94
View File
@@ -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
}
+324
View File
@@ -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
}
+231
View File
@@ -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
}
+215
View File
@@ -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
}
-331
View File
@@ -1,331 +0,0 @@
package firewall
import (
"fmt"
"net"
"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/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
}
if err := f.reloadPortKnocking(chain); 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 (f *firewall) reloadPortKnocking(chain chain.LocalInput) error {
if len(f.config.PortKnocking) == 0 {
return nil
}
portKnocking, err := f.chains.NewPortKnocking("port_knocking")
if err != nil {
return err
}
for _, portKnockingConfig := range f.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 := chain.AddRule(expr...); err != nil {
return err
}
}
if err := portKnocking.AddRuleIn(chain.AddRule); 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
}
-244
View File
@@ -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
}
+10
View File
@@ -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)
}
+89
View File
@@ -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
}
+56
View File
@@ -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)
}
+49
View File
@@ -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
}
+22
View File
@@ -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
}
+4 -4
View File
@@ -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
}
+10 -1
View File
@@ -2,6 +2,7 @@ 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"
@@ -10,6 +11,7 @@ import (
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"
@@ -17,7 +19,7 @@ import (
)
func NewDaemon(
info DaemonInfo,
info info.Info,
opts DaemonOptions,
logger log.Logger,
notifications notifications.Notifications,
@@ -40,6 +42,9 @@ func NewDaemon(
}
blockingService := blocking.New(opts.Repositories.Blocking(), logger)
dataDirForFirewall := strings.TrimRight(opts.DataDir, "/") + "/firewall"
firewall, err := firewall2.New(
opts.PathNftables,
blockingService,
@@ -47,7 +52,11 @@ func NewDaemon(
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, geoIPService.Info)
+1 -1
View File
@@ -69,7 +69,7 @@
"user": "User",
"access to user has been gained": "Access to user has been gained",
"unknown": "unknown",
"blockSec": "Blocked for {{.BlockSec}} seconds",
"blockSec": "Blocked for {{.BlockSec}}",
"ports": "Ports: {{.Ports}}",
"alert.subject": "Alert detected ({{.Name}}) (group:{{.GroupName}})",
+1 -1
View File
@@ -69,7 +69,7 @@
"user": "Пайдаланушы",
"access to user has been gained": "Пайдаланушыға кіру мүмкіндігі алынды",
"unknown": "белгісіз",
"blockSec": "{{.BlockSec}} секундқа блокталды",
"blockSec": "{{.BlockSec}} блокталды",
"ports": "Порттар: {{.Ports}}",
"alert.subject": "Ескерту анықталды ({{.Name}}) (топ:{{.GroupName}})",
+1 -1
View File
@@ -69,7 +69,7 @@
"user": "Пользователь",
"access to user has been gained": "Получен доступ к пользователю",
"unknown": "неизвестный",
"blockSec": "Блокировка на {{.BlockSec}} секунд",
"blockSec": "Блокировка на {{.BlockSec}}",
"ports": "Порты: {{.Ports}}",
"alert.subject": "Обнаружено оповещение ({{.Name}}) (группа:{{.GroupName}})",
+26
View File
@@ -1,7 +1,10 @@
package filesystem
import (
"crypto/sha256"
"encoding/hex"
"errors"
"io"
"os"
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/i18n"
@@ -18,3 +21,26 @@ func FileHasWritePermissions(path string) error {
return nil
}
func FileExists(path string) bool {
_, err := os.Stat(path)
return err == nil
}
func FileChecksum(path string) (string, error) {
file, err := os.Open(path)
if err != nil {
return "", err
}
defer func() {
_ = file.Close()
}()
hash := sha256.New()
if _, err := io.Copy(hash, file); err != nil {
return "", err
}
return hex.EncodeToString(hash.Sum(nil)), nil
}
+33 -1
View File
@@ -1,6 +1,9 @@
package format
import "fmt"
import (
"fmt"
"time"
)
func HumanBytes(n uint64) string {
const (
@@ -23,3 +26,32 @@ func HumanBytes(n uint64) string {
return fmt.Sprintf("%d B", n)
}
}
func HumanDuration(d time.Duration) string {
d = d.Round(time.Second)
days := d / (24 * time.Hour)
d -= days * 24 * time.Hour
hours := d / time.Hour
d -= hours * time.Hour
minutes := d / time.Minute
d -= minutes * time.Minute
seconds := d / time.Second
if days > 0 {
return fmt.Sprintf("%dd %02dh %02dm %02ds", days, hours, minutes, seconds)
}
if hours > 0 {
return fmt.Sprintf("%dh %02dm %02ds", hours, minutes, seconds)
}
if minutes > 0 {
return fmt.Sprintf("%dm %02ds", minutes, seconds)
}
return fmt.Sprintf("%ds", seconds)
}
+4 -4
View File
@@ -3,7 +3,7 @@ package firewall
import (
"fmt"
"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/config"
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/setting/validate"
"github.com/spf13/viper"
)
@@ -75,7 +75,7 @@ func (s Setting) Validate() error {
return nil
}
func (s Setting) ToPorts() (InPorts []firewall.ConfigPort, OutPorts []firewall.ConfigPort, error error) {
func (s Setting) ToPorts() (InPorts []config.ConfigPort, OutPorts []config.ConfigPort, error error) {
for _, port := range s.Ports {
addInPorts, addOutPorts, err := port.ToPorts()
if err != nil {
@@ -106,8 +106,8 @@ func (s Setting) ToIPs() (IPs IPs, error error) {
return
}
func (s Setting) ToConfigPortKnocking() ([]firewall.ConfigPortKnocking, error) {
var configPortKnocking []firewall.ConfigPortKnocking
func (s Setting) ToConfigPortKnocking() ([]config.ConfigPortKnocking, error) {
var configPortKnocking []config.ConfigPortKnocking
portKnockingNames := make(map[string]string)
+9 -9
View File
@@ -3,7 +3,7 @@ package firewall
import (
"errors"
"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/config"
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/types"
port2 "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"
@@ -23,11 +23,11 @@ func defaultIPs() []IP {
}
type IPs struct {
InIP4 []firewall.ConfigIP
OutIP4 []firewall.ConfigIP
InIP4 []config.ConfigIP
OutIP4 []config.ConfigIP
InIP6 []firewall.ConfigIP
OutIP6 []firewall.ConfigIP
InIP6 []config.ConfigIP
OutIP6 []config.ConfigIP
}
func (i *IP) ToIPs() (IPs IPs, error error) {
@@ -48,7 +48,7 @@ func (i *IP) ToIPs() (IPs IPs, error error) {
return
}
baseConfigIP := firewall.ConfigIP{
baseConfigIP := config.ConfigIP{
IP: ipNet,
Action: action,
LimitRate: i.LimitRate,
@@ -89,7 +89,7 @@ func (i *IP) validate() error {
return nil
}
func loopIP(baseConfigIP firewall.ConfigIP, directions []string, protocols []string, ports []int) (in []firewall.ConfigIP, out []firewall.ConfigIP, error error) {
func loopIP(baseConfigIP config.ConfigIP, directions []string, protocols []string, ports []int) (in []config.ConfigIP, out []config.ConfigIP, error error) {
for _, direction := range directions {
addDirection, err := port2.ToDirection(direction)
if err != nil {
@@ -136,7 +136,7 @@ func loopIP(baseConfigIP firewall.ConfigIP, directions []string, protocols []str
return
}
func loopIPProtocol(baseConfigIP firewall.ConfigIP, protocols []string, ports []int, direction types.Direction) (in []firewall.ConfigIP, out []firewall.ConfigIP, error error) {
func loopIPProtocol(baseConfigIP config.ConfigIP, protocols []string, ports []int, direction types.Direction) (in []config.ConfigIP, out []config.ConfigIP, error error) {
for _, protocol := range protocols {
addProtocol, err := port2.ToProtocol(protocol)
if err != nil {
@@ -169,7 +169,7 @@ func loopIPProtocol(baseConfigIP firewall.ConfigIP, protocols []string, ports []
return
}
func loopIPPort(baseConfigIP firewall.ConfigIP, ports []int, direction types.Direction, protocol types.Protocol) (in []firewall.ConfigIP, out []firewall.ConfigIP, error error) {
func loopIPPort(baseConfigIP config.ConfigIP, ports []int, direction types.Direction, protocol types.Protocol) (in []config.ConfigIP, out []config.ConfigIP, error error) {
for _, port := range ports {
if err := validate.Port(port, "port"); err != nil {
error = err
+7 -5
View File
@@ -4,11 +4,12 @@ import (
"errors"
"strings"
"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/config"
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/setting/validate"
)
type options struct {
Cache bool `mapstructure:"cache"`
ClearMode string `mapstructure:"clear_mode"`
SavesRules bool `mapstructure:"saves_rules"`
SavesRulesPath string `mapstructure:"saves_rules_path"`
@@ -19,6 +20,7 @@ type options struct {
func defaultOptions() options {
return options{
Cache: true,
ClearMode: "global",
SavesRules: false,
SavesRulesPath: "/etc/nftables.conf",
@@ -49,13 +51,13 @@ func (o options) ValidateSavesRulesPath() error {
return nil
}
func (o options) ToClearMode() (firewall.ClearMode, error) {
func (o options) ToClearMode() (config.ClearMode, error) {
switch o.ClearMode {
case "global":
return firewall.ClearModeGlobal, nil
return config.ClearModeGlobal, nil
case "own":
return firewall.ClearModeOwn, nil
return config.ClearModeOwn, nil
}
return firewall.ClearModeGlobal, errors.New("invalid option clear_mode. Must be 'global' or 'own'")
return config.ClearModeGlobal, errors.New("invalid option clear_mode. Must be 'global' or 'own'")
}
+6 -6
View File
@@ -3,7 +3,7 @@ package firewall
import (
"fmt"
"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/config"
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/types"
)
@@ -33,23 +33,23 @@ func defaultPolicy() policy {
}
}
func (p policy) ToConfigPolicy() (firewall.ConfigPolicy, error) {
func (p policy) ToConfigPolicy() (config.ConfigPolicy, error) {
inputDrop, err := p.dropToPolicyDrop(p.InputDrop, "input_drop")
if err != nil {
return firewall.ConfigPolicy{}, err
return config.ConfigPolicy{}, err
}
outputDrop, err := p.dropToPolicyDrop(p.OutputDrop, "output_drop")
if err != nil {
return firewall.ConfigPolicy{}, err
return config.ConfigPolicy{}, err
}
forwardDrop, err := p.dropToPolicyDrop(p.ForwardDrop, "forward_drop")
if err != nil {
return firewall.ConfigPolicy{}, err
return config.ConfigPolicy{}, err
}
return firewall.ConfigPolicy{
return config.ConfigPolicy{
DefaultAllowInput: p.DefaultAllowInput,
DefaultAllowOutput: p.DefaultAllowOutput,
DefaultAllowForward: p.DefaultAllowForward,
+3 -3
View File
@@ -3,7 +3,7 @@ package firewall
import (
"errors"
"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/config"
"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"
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/setting/validate"
@@ -21,7 +21,7 @@ func defaultPorts() []Port {
return []Port{}
}
func (p *Port) ToPorts() (InPorts []firewall.ConfigPort, OutPorts []firewall.ConfigPort, error error) {
func (p *Port) ToPorts() (InPorts []config.ConfigPort, OutPorts []config.ConfigPort, error error) {
if err := p.validate(); err != nil {
error = err
return
@@ -56,7 +56,7 @@ func (p *Port) ToPorts() (InPorts []firewall.ConfigPort, OutPorts []firewall.Con
return
}
addPort := firewall.ConfigPort{
addPort := config.ConfigPort{
Port: l4Port,
Action: action,
LimitRate: p.LimitRate,
+10 -10
View File
@@ -4,7 +4,7 @@ import (
"fmt"
"strings"
"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/config"
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/types"
port2 "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"
@@ -22,40 +22,40 @@ func defaultPortKnocking() []portKnocking {
return []portKnocking{}
}
func (p *portKnocking) ToPortKnocking() (firewall.ConfigPortKnocking, error) {
func (p *portKnocking) ToPortKnocking() (config.ConfigPortKnocking, error) {
if len(p.Knocks) == 0 {
return firewall.ConfigPortKnocking{}, fmt.Errorf("port knocking must have at least one knock")
return config.ConfigPortKnocking{}, fmt.Errorf("port knocking must have at least one knock")
}
if err := p.validate(); err != nil {
return firewall.ConfigPortKnocking{}, err
return config.ConfigPortKnocking{}, err
}
protocol, err := port2.ToProtocol(p.Protocol)
if err != nil {
return firewall.ConfigPortKnocking{}, err
return config.ConfigPortKnocking{}, err
}
l4Port, err := types.NewL4Port(uint16(p.Port), protocol)
if err != nil {
return firewall.ConfigPortKnocking{}, err
return config.ConfigPortKnocking{}, err
}
ipVersion, err := toVersionIP(p.IPVersion)
if err != nil {
return firewall.ConfigPortKnocking{}, err
return config.ConfigPortKnocking{}, err
}
knocks := make([]*firewall.ConfigKnock, 0, len(p.Knocks))
knocks := make([]*config.ConfigKnock, 0, len(p.Knocks))
for _, knock := range p.Knocks {
knock, err := knock.ToKnock()
if err != nil {
return firewall.ConfigPortKnocking{}, err
return config.ConfigPortKnocking{}, err
}
knocks = append(knocks, &knock)
}
return firewall.ConfigPortKnocking{
return config.ConfigPortKnocking{
Name: p.Name,
Port: l4Port,
IPVersion: ipVersion,
@@ -3,7 +3,7 @@ package firewall
import (
"fmt"
"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/config"
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/types"
port2 "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"
@@ -16,26 +16,26 @@ type portKnockingKnock struct {
Action string `mapstructure:"action"`
}
func (k *portKnockingKnock) ToKnock() (firewall.ConfigKnock, error) {
func (k *portKnockingKnock) ToKnock() (config.ConfigKnock, error) {
if err := k.validate(); err != nil {
return firewall.ConfigKnock{}, err
return config.ConfigKnock{}, err
}
protocol, err := port2.ToProtocol(k.Protocol)
if err != nil {
return firewall.ConfigKnock{}, err
return config.ConfigKnock{}, err
}
l4Port, err := types.NewL4Port(uint16(k.Port), protocol)
if err != nil {
return firewall.ConfigKnock{}, err
return config.ConfigKnock{}, err
}
action, err := port2.ToKnockAction(k.Action)
if err != nil {
return firewall.ConfigKnock{}, err
return config.ConfigKnock{}, err
}
return firewall.ConfigKnock{
return config.ConfigKnock{
Port: l4Port,
Action: action,
Timeout: uint32(k.Timeout),
+1 -1
View File
@@ -31,7 +31,7 @@ func InitSetting(path string) error {
}
// Default config
Config = settingDefault()
Config = settingDefault(path)
v := viper.New()
v.SetConfigType("toml")
+33 -21
View File
@@ -3,10 +3,10 @@ package setting
import (
"errors"
"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/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"
firewallConfig "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/config"
"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"
@@ -42,41 +42,41 @@ func otherSettingsPathDefault() *otherSettingsPath {
}
}
func (o *otherSettingsPath) ToFirewallConfig(dockerSupport bool) (firewall.Config, error) {
func (o *otherSettingsPath) ToFirewallConfig(dockerSupport bool) (firewallConfig.Config, error) {
setting, err := firewallSetting.InitSetting(o.Firewall)
if err != nil {
return firewall.Config{}, err
return firewallConfig.Config{}, err
}
configPolicy, err := setting.Policy.ToConfigPolicy()
if err != nil {
return firewall.Config{}, err
return firewallConfig.Config{}, err
}
inPorts, outPorts, err := setting.ToPorts()
if err != nil {
return firewall.Config{}, err
return firewallConfig.Config{}, err
}
IPs, err := setting.ToIPs()
if err != nil {
return firewall.Config{}, err
return firewallConfig.Config{}, err
}
optionClearMode, err := setting.Options.ToClearMode()
if err != nil {
return firewall.Config{}, err
return firewallConfig.Config{}, err
}
portKnocking, err := setting.ToConfigPortKnocking()
if err != nil {
return firewall.Config{}, err
return firewallConfig.Config{}, err
}
return firewall.Config{
return firewallConfig.Config{
InPorts: inPorts,
OutPorts: outPorts,
IP4: firewall.ConfigIP4{
IP4: firewallConfig.ConfigIP4{
IcmpIn: setting.IP4.IcmpIn,
IcmpInRate: setting.IP4.IcmpInRate,
IcmpOut: setting.IP4.IcmpOut,
@@ -85,13 +85,14 @@ func (o *otherSettingsPath) ToFirewallConfig(dockerSupport bool) (firewall.Confi
InIPs: IPs.InIP4,
OutIPs: IPs.OutIP4,
},
IP6: firewall.ConfigIP6{
IP6: firewallConfig.ConfigIP6{
Enable: setting.IP6.Enable,
IcmpStrict: setting.IP6.IcmpStrict,
InIPs: IPs.InIP6,
OutIPs: IPs.OutIP6,
},
Options: firewall.ConfigOptions{
Options: firewallConfig.ConfigOptions{
Cache: setting.Options.Cache,
ClearMode: optionClearMode,
SavesRules: setting.Options.SavesRules,
SavesRulesPath: setting.Options.SavesRulesPath,
@@ -100,7 +101,7 @@ func (o *otherSettingsPath) ToFirewallConfig(dockerSupport bool) (firewall.Confi
PacketFilter: setting.Options.PacketFilter,
DockerSupport: dockerSupport,
},
MetadataNaming: firewall.ConfigMetadata{
MetadataNaming: firewallConfig.ConfigMetadata{
TableName: setting.MetadataNaming.TableName,
ChainInputName: setting.MetadataNaming.ChainInputName,
ChainOutputName: setting.MetadataNaming.ChainOutputName,
@@ -149,32 +150,32 @@ func (o *otherSettingsPath) ToNotificationsConfig() (notifications.Config, error
}, nil
}
func (o *otherSettingsPath) ToAnalyzerConfig(binaryLocations *binaryLocations) (config.Config, error) {
func (o *otherSettingsPath) ToAnalyzerConfig(binaryLocations *binaryLocations) (analyzerConfig.Config, error) {
if binaryLocations.Journalctl == "" {
return config.Config{}, errors.New(i18n.Lang.T("parameter is not specified", map[string]any{
return analyzerConfig.Config{}, errors.New(i18n.Lang.T("parameter is not specified", map[string]any{
"Parameter": "binaryLocations.journalctl",
}))
}
setting, err := analyzerSetting.InitSetting(o.Analyzer)
if err != nil {
return config.Config{}, err
return analyzerConfig.Config{}, err
}
if err := setting.Validate(); err != nil {
return config.Config{}, err
return analyzerConfig.Config{}, err
}
binPath := config.BinPath{
binPath := analyzerConfig.BinPath{
Journalctl: binaryLocations.Journalctl,
}
sources, err := setting.ToSources()
if err != nil {
return config.Config{}, err
return analyzerConfig.Config{}, err
}
return config.Config{
return analyzerConfig.Config{
BinPath: binPath,
Sources: sources,
}, nil
@@ -239,3 +240,14 @@ func (o *otherSettingsPath) ToConfig(dataDir string, logger logger.Logger) (geoI
return geoIPService, setting.Enabled, nil
}
func (o *otherSettingsPath) ListPathFiles() map[string]string {
return map[string]string{
"firewall": o.Firewall,
"notifications": o.Notifications,
"analyzer": o.Analyzer,
"docker": o.Docker,
"blocklists": o.Blocklists,
"geoip": o.GeoIP,
}
}
+11 -1
View File
@@ -10,6 +10,8 @@ import (
)
type setting struct {
ConfigPath string
Testing bool `mapstructure:"testing"`
TestingInterval int16 `mapstructure:"testing_interval"`
Language string `mapstructure:"language"`
@@ -23,8 +25,10 @@ type setting struct {
OtherSettingsPath *otherSettingsPath
}
func settingDefault() *setting {
func settingDefault(configPath string) *setting {
return &setting{
ConfigPath: configPath,
Testing: true,
TestingInterval: 5,
Language: "ru",
@@ -148,3 +152,9 @@ func (s setting) validateSocketFile() error {
}
return nil
}
func (s setting) ListPathConfigFiles() map[string]string {
filePaths := s.OtherSettingsPath.ListPathFiles()
filePaths["kor-elf-shield"] = s.ConfigPath
return filePaths
}