Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 72a0f941a1 | |||
|
e9cd163ae7
|
|||
|
f778e25575
|
|||
|
21ab1802fe
|
|||
|
62517e6f4c
|
|||
|
8a7608dac6
|
|||
|
8d90a3770d
|
|||
|
4acb81b5a7
|
|||
|
49ab9c48c7
|
|||
|
28019ec171
|
|||
|
03b4009f96
|
|||
|
71502ff0c9
|
|||
|
ab59b356dc
|
|||
|
8595c6791d
|
|||
|
c64c94dceb
|
|||
|
a387e85569
|
|||
|
ab8466ada2
|
|||
|
d978343f4c
|
|||
|
14a6b9df0b
|
|||
|
298c7140a4
|
|||
|
527b6c8264
|
|||
|
0d707ac3c6
|
|||
|
b535195c1f
|
|||
|
3c12429f0e
|
|||
|
25ee39c0ec
|
|||
|
95e1f274f7
|
|||
|
5f72efd1bf
|
|||
|
e8826cb86b
|
|||
|
f12097b280
|
|||
|
3c040945bc
|
|||
|
0fdc07c0af
|
|||
|
a1345bd3e1
|
|||
|
d2f3640b75
|
|||
|
1363ff4bef
|
@@ -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-адресах (континент, страна, город, часовой пояс).
|
||||
|
||||
@@ -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" если вы уверены в своих действиях.
|
||||
|
||||
@@ -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,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=
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
type Config struct {
|
||||
BlocklistRepository repository.BlocklistRepository
|
||||
Sources []*SourceConfig
|
||||
PathDir string
|
||||
}
|
||||
|
||||
type SourceConfig struct {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package entity
|
||||
|
||||
const (
|
||||
MetadataKeyVersion = "Version"
|
||||
MetadataKeyFirewallFileNft = "firewall-file-nft" // checksum of the firewall file
|
||||
)
|
||||
|
||||
type Metadata struct {
|
||||
Value string `json:"Value"`
|
||||
}
|
||||
|
||||
func KeySetting(name string) string {
|
||||
return "setting-" + name
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db/entity"
|
||||
"go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
type 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
|
||||
}
|
||||
@@ -3,16 +3,14 @@ package docker_monitor
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/client"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/firewall"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/rule_strategy"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
)
|
||||
|
||||
type Docker interface {
|
||||
NftReload(newNoneChain func(chain string) (nftChain.Chain, error)) error
|
||||
NftChains() chain.Chains
|
||||
NftReload(nftDocker firewall.NFTDocker) error
|
||||
Run()
|
||||
Close() error
|
||||
}
|
||||
@@ -39,12 +37,8 @@ func New(config *Config, ctx context.Context, logger log.Logger) (Docker, error)
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *docker) NftReload(newNoneChain func(chain string) (nftChain.Chain, error)) error {
|
||||
return d.ruleStrategy.Reload(newNoneChain)
|
||||
}
|
||||
|
||||
func (d *docker) NftChains() chain.Chains {
|
||||
return d.ruleStrategy.Chains()
|
||||
func (d *docker) NftReload(nftDocker firewall.NFTDocker) error {
|
||||
return d.ruleStrategy.Reload(nftDocker)
|
||||
}
|
||||
|
||||
func (d *docker) Run() {
|
||||
@@ -66,9 +60,3 @@ func (d *docker) Run() {
|
||||
func (d *docker) Close() error {
|
||||
return d.dockerClient.EventsClose()
|
||||
}
|
||||
|
||||
func (d *docker) chainCommand(chainData chain.Data, rule string) {
|
||||
if err := chainData.AddRule(rule); err != nil {
|
||||
d.logger.Error(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +1,23 @@
|
||||
package docker_monitor
|
||||
|
||||
import (
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/chain"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/firewall"
|
||||
)
|
||||
|
||||
type DockerNotSupport struct {
|
||||
chains chain.Chains
|
||||
}
|
||||
type dockerNotSupport struct{}
|
||||
|
||||
func NewDockerNotSupport() Docker {
|
||||
return &DockerNotSupport{
|
||||
chains: chain.NewEmptyChains(),
|
||||
}
|
||||
return &dockerNotSupport{}
|
||||
}
|
||||
|
||||
func (d *DockerNotSupport) NftReload(_ func(chain string) (nftChain.Chain, error)) error {
|
||||
func (d *dockerNotSupport) NftReload(_ firewall.NFTDocker) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DockerNotSupport) NftChains() chain.Chains {
|
||||
return d.chains
|
||||
}
|
||||
|
||||
func (d *DockerNotSupport) Run() {
|
||||
func (d *dockerNotSupport) Run() {
|
||||
|
||||
}
|
||||
|
||||
func (d *DockerNotSupport) Close() error {
|
||||
func (d *dockerNotSupport) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
package firewall
|
||||
|
||||
import (
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
nftFirewall "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/chain"
|
||||
)
|
||||
|
||||
type NFTDocker interface {
|
||||
Chains() NFTDockerChains
|
||||
NFT() nftFirewall.NFT
|
||||
}
|
||||
|
||||
type NFTDockerChains interface {
|
||||
List() []chain.Docker
|
||||
|
||||
ForwardFilter() chain.Docker
|
||||
ForwardBridge() chain.Docker
|
||||
ForwardCT() chain.Docker
|
||||
|
||||
PreroutingFilter() chain.Docker
|
||||
DockerFilter() chain.Docker
|
||||
DockerFilterFirst() chain.Docker
|
||||
DockerFilterSecond() chain.Docker
|
||||
|
||||
DockerNat() chain.Docker
|
||||
PostroutingNat() chain.Docker
|
||||
}
|
||||
|
||||
type nftDocker struct {
|
||||
chains NFTDockerChains
|
||||
nft nftFirewall.NFT
|
||||
}
|
||||
|
||||
func NewNFT(nft nftFirewall.NFT, chains NFTDockerChains) NFTDocker {
|
||||
return &nftDocker{
|
||||
chains: chains,
|
||||
nft: nft,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *nftDocker) NFT() nftFirewall.NFT {
|
||||
return n.nft
|
||||
}
|
||||
|
||||
func (n *nftDocker) Chains() NFTDockerChains {
|
||||
return n.chains
|
||||
}
|
||||
|
||||
type nftDockerChains struct {
|
||||
forwardFilter chain.Docker
|
||||
forwardBridge chain.Docker
|
||||
forwardCT chain.Docker
|
||||
|
||||
preroutingFilter chain.Docker
|
||||
dockerFilter chain.Docker
|
||||
dockerFilterFirst chain.Docker
|
||||
dockerFilterSecond chain.Docker
|
||||
|
||||
dockerNat chain.Docker
|
||||
postroutingNat chain.Docker
|
||||
}
|
||||
|
||||
func NewNFTChains(nft nftFirewall.NFT, family family.Type, table string) NFTDockerChains {
|
||||
return &nftDockerChains{
|
||||
forwardFilter: chain.NewDocker(nft.NFT(), family, table, "docker_forward_filter"),
|
||||
forwardBridge: chain.NewDocker(nft.NFT(), family, table, "docker_forward_bridge"),
|
||||
forwardCT: chain.NewDocker(nft.NFT(), family, table, "docker_forward_ct"),
|
||||
|
||||
preroutingFilter: chain.NewDocker(nft.NFT(), family, table, "docker_prerouting_filter"),
|
||||
dockerFilter: chain.NewDocker(nft.NFT(), family, table, "docker_filter"),
|
||||
dockerFilterFirst: chain.NewDocker(nft.NFT(), family, table, "docker_filter_first"),
|
||||
dockerFilterSecond: chain.NewDocker(nft.NFT(), family, table, "docker_filter_second"),
|
||||
|
||||
dockerNat: chain.NewDocker(nft.NFT(), family, table, "docker_nat"),
|
||||
postroutingNat: chain.NewDocker(nft.NFT(), family, table, "docker_postrouting_nat"),
|
||||
}
|
||||
}
|
||||
|
||||
func (n *nftDockerChains) ForwardFilter() chain.Docker {
|
||||
return n.forwardFilter
|
||||
}
|
||||
|
||||
func (n *nftDockerChains) ForwardBridge() chain.Docker {
|
||||
return n.forwardBridge
|
||||
}
|
||||
|
||||
func (n *nftDockerChains) ForwardCT() chain.Docker {
|
||||
return n.forwardCT
|
||||
}
|
||||
|
||||
func (n *nftDockerChains) PreroutingFilter() chain.Docker {
|
||||
return n.preroutingFilter
|
||||
}
|
||||
|
||||
func (n *nftDockerChains) DockerFilter() chain.Docker {
|
||||
return n.dockerFilter
|
||||
}
|
||||
|
||||
func (n *nftDockerChains) DockerFilterFirst() chain.Docker {
|
||||
return n.dockerFilterFirst
|
||||
}
|
||||
|
||||
func (n *nftDockerChains) DockerFilterSecond() chain.Docker {
|
||||
return n.dockerFilterSecond
|
||||
}
|
||||
|
||||
func (n *nftDockerChains) DockerNat() chain.Docker {
|
||||
return n.dockerNat
|
||||
}
|
||||
|
||||
func (n *nftDockerChains) PostroutingNat() chain.Docker {
|
||||
return n.postroutingNat
|
||||
}
|
||||
|
||||
func (n *nftDockerChains) List() []chain.Docker {
|
||||
return []chain.Docker{
|
||||
n.forwardFilter,
|
||||
n.forwardBridge,
|
||||
n.forwardCT,
|
||||
n.preroutingFilter,
|
||||
n.dockerFilter,
|
||||
n.dockerFilterFirst,
|
||||
n.dockerFilterSecond,
|
||||
n.dockerNat,
|
||||
n.postroutingNat,
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ func newRuleStrategy(config *Config, dockerClient client.Docker, logger log.Logg
|
||||
|
||||
switch config.RuleStrategy {
|
||||
case RuleStrategyRebuild:
|
||||
return rule_strategy.NewRebuildStrategy(generate), nil
|
||||
return rule_strategy.NewRebuildStrategy(generate, logger), nil
|
||||
case RuleStrategyIncremental:
|
||||
return rule_strategy.NewIncrementalStrategy(generate, dockerClient, logger), nil
|
||||
}
|
||||
|
||||
@@ -3,17 +3,19 @@ package rule_strategy
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/chain"
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/client"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/firewall"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
)
|
||||
|
||||
type Generator interface {
|
||||
GenerateAll(chains chain.Chains, isComment bool)
|
||||
GenerateBridge(bridge client.Bridge, chain chain.Chains, isComment bool)
|
||||
GenerateContainer(container client.Container, bridgeName string, chain chain.Chains, isComment bool)
|
||||
ClearChains(chains chain.Chains)
|
||||
AddRule(chainData chain.Data, rule string)
|
||||
GenerateAll(builder nft.BatchBuilder, chains firewall.NFTDockerChains, isComment bool)
|
||||
GenerateBridge(bridge client.Bridge, builder nft.BatchBuilder, chains firewall.NFTDockerChains, isComment bool)
|
||||
GenerateContainer(container client.Container, bridgeName string, builder nft.BatchBuilder, chains firewall.NFTDockerChains, isComment bool)
|
||||
ClearChains(builder nft.BatchBuilder, chains firewall.NFTDockerChains)
|
||||
AddRule(builder nft.BatchBuilder, chainDocker chain.Docker, rule string)
|
||||
}
|
||||
|
||||
type generator struct {
|
||||
@@ -28,19 +30,17 @@ func NewGenerator(dockerClient client.Docker, logger log.Logger) Generator {
|
||||
}
|
||||
}
|
||||
|
||||
func (g *generator) GenerateAll(chains chain.Chains, isComment bool) {
|
||||
listChains := chains.List()
|
||||
|
||||
if err := listChains.ForwardCT.JumpTo(&listChains.ForwardFilter, "", ""); err != nil {
|
||||
func (g *generator) GenerateAll(builder nft.BatchBuilder, chains firewall.NFTDockerChains, isComment bool) {
|
||||
if err := chains.ForwardCT().JumpTo(builder, chains.ForwardFilter(), "", ""); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
if err := listChains.ForwardBridge.JumpTo(&listChains.ForwardFilter, "", ""); err != nil {
|
||||
if err := chains.ForwardBridge().JumpTo(builder, chains.ForwardFilter(), "", ""); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
if err := listChains.DockerFilterFirst.JumpTo(&listChains.DockerFilter, "", ""); err != nil {
|
||||
if err := chains.DockerFilterFirst().JumpTo(builder, chains.DockerFilter(), "", ""); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
if err := listChains.DockerFilterSecond.JumpTo(&listChains.DockerFilter, "", ""); err != nil {
|
||||
if err := chains.DockerFilterSecond().JumpTo(builder, chains.DockerFilter(), "", ""); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
|
||||
@@ -51,20 +51,18 @@ func (g *generator) GenerateAll(chains chain.Chains, isComment bool) {
|
||||
}
|
||||
|
||||
for _, bridge := range bridges {
|
||||
g.GenerateBridge(bridge, chains, isComment)
|
||||
g.GenerateBridge(bridge, builder, chains, isComment)
|
||||
|
||||
if bridge.Containers == nil {
|
||||
continue
|
||||
}
|
||||
for _, container := range bridge.Containers {
|
||||
g.GenerateContainer(container, bridge.Name, chains, isComment)
|
||||
g.GenerateContainer(container, bridge.Name, builder, chains, isComment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *generator) GenerateBridge(bridge client.Bridge, chain chain.Chains, isComment bool) {
|
||||
listChains := chain.List()
|
||||
|
||||
func (g *generator) GenerateBridge(bridge client.Bridge, builder nft.BatchBuilder, chains firewall.NFTDockerChains, isComment bool) {
|
||||
var rule string
|
||||
comment := ""
|
||||
if isComment {
|
||||
@@ -72,27 +70,26 @@ func (g *generator) GenerateBridge(bridge client.Bridge, chain chain.Chains, isC
|
||||
}
|
||||
|
||||
rule = fmt.Sprintf("iifname != \"%s\" oifname \"%s\" counter drop %s", bridge.Name, bridge.Name, comment)
|
||||
g.AddRule(listChains.DockerFilterSecond, rule)
|
||||
g.AddRule(builder, chains.DockerFilterSecond(), rule)
|
||||
|
||||
rule = fmt.Sprintf("iifname \"%s\" counter accept %s", bridge.Name, comment)
|
||||
g.AddRule(listChains.ForwardFilter, rule)
|
||||
g.AddRule(builder, chains.ForwardFilter(), rule)
|
||||
|
||||
rule = fmt.Sprintf("oifname \"%s\" counter", bridge.Name)
|
||||
if err := listChains.DockerFilter.JumpTo(&listChains.ForwardBridge, rule, comment); err != nil {
|
||||
if err := chains.DockerFilter().JumpTo(builder, chains.ForwardBridge(), rule, comment); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
|
||||
rule = fmt.Sprintf("oifname \"%s\" ct state related,established counter accept %s", bridge.Name, comment)
|
||||
g.AddRule(listChains.ForwardCT, rule)
|
||||
g.AddRule(builder, chains.ForwardCT(), rule)
|
||||
|
||||
for _, subnet := range bridge.Subnets {
|
||||
rule = fmt.Sprintf("ip saddr %s oifname != \"%s\" counter masquerade %s", subnet, bridge.Name, comment)
|
||||
g.AddRule(listChains.PostroutingNat, rule)
|
||||
g.AddRule(builder, chains.PostroutingNat(), rule)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *generator) GenerateContainer(container client.Container, bridgeName string, chain chain.Chains, isComment bool) {
|
||||
listChains := chain.List()
|
||||
func (g *generator) GenerateContainer(container client.Container, bridgeName string, builder nft.BatchBuilder, chains firewall.NFTDockerChains, isComment bool) {
|
||||
var rule string
|
||||
comment := ""
|
||||
if isComment {
|
||||
@@ -101,14 +98,14 @@ func (g *generator) GenerateContainer(container client.Container, bridgeName str
|
||||
|
||||
for _, ipInfo := range container.Networks.IPAddresses {
|
||||
rule = fmt.Sprintf("%s daddr %s iifname != \"%s\" counter drop %s", ipInfo.NftPrefix(), ipInfo.Address, bridgeName, comment)
|
||||
g.AddRule(listChains.PreroutingFilter, rule)
|
||||
g.AddRule(builder, chains.PreroutingFilter(), rule)
|
||||
|
||||
for _, port := range container.Networks.Ports {
|
||||
isZeroAddress := false
|
||||
for _, hostInfo := range port.HostPort {
|
||||
if hostInfo.IP.Address != "0.0.0.0" && hostInfo.IP.Address != "::" && (hostInfo.IP.Address == "127.0.0.1" || hostInfo.IP.Address == "::1") {
|
||||
rule = fmt.Sprintf("%s daddr %s iifname != \"lo\" %s dport %s counter drop %s", hostInfo.IP.NftPrefix(), hostInfo.IP.Address, port.Protocol, hostInfo.Port, comment)
|
||||
g.AddRule(listChains.PreroutingFilter, rule)
|
||||
g.AddRule(builder, chains.PreroutingFilter(), rule)
|
||||
}
|
||||
|
||||
if hostInfo.IP.Address == "0.0.0.0" || hostInfo.IP.Address == "::" {
|
||||
@@ -117,58 +114,32 @@ func (g *generator) GenerateContainer(container client.Container, bridgeName str
|
||||
}
|
||||
isZeroAddress = true
|
||||
rule = fmt.Sprintf("iifname != \"%s\" %s dport %s counter dnat %s to %s:%s %s", bridgeName, port.Protocol, hostInfo.Port, ipInfo.NftPrefix(), ipInfo.Address, port.Port, comment)
|
||||
g.AddRule(listChains.DockerNat, rule)
|
||||
g.AddRule(builder, chains.DockerNat(), rule)
|
||||
|
||||
rule = fmt.Sprintf("%s daddr %s iifname != \"%s\" oifname \"%s\" %s dport %s counter accept %s", ipInfo.NftPrefix(), ipInfo.Address, bridgeName, bridgeName, port.Protocol, port.Port, comment)
|
||||
g.AddRule(listChains.DockerFilterFirst, rule)
|
||||
g.AddRule(builder, chains.DockerFilterFirst(), rule)
|
||||
continue
|
||||
}
|
||||
rule = fmt.Sprintf("%s daddr %s iifname != \"%s\" oifname \"%s\" %s dport %s counter accept %s", ipInfo.NftPrefix(), ipInfo.Address, bridgeName, bridgeName, port.Protocol, port.Port, comment)
|
||||
g.AddRule(listChains.DockerFilterFirst, rule)
|
||||
g.AddRule(builder, chains.DockerFilterFirst(), rule)
|
||||
|
||||
rule = fmt.Sprintf("%s daddr %s iifname != \"%s\" %s dport %s counter dnat to %s:%s %s", hostInfo.IP.NftPrefix(), hostInfo.IP.Address, bridgeName, port.Protocol, hostInfo.Port, ipInfo.Address, port.Port, comment)
|
||||
g.AddRule(listChains.DockerNat, rule)
|
||||
g.AddRule(builder, chains.DockerNat(), rule)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *generator) ClearChains(chains chain.Chains) {
|
||||
listChains := chains.List()
|
||||
|
||||
if err := listChains.DockerNat.Clear(); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
if err := listChains.PostroutingNat.Clear(); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
if err := listChains.PreroutingFilter.Clear(); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
if err := listChains.DockerFilter.Clear(); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
if err := listChains.DockerFilterFirst.Clear(); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
if err := listChains.DockerFilterSecond.Clear(); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
if err := listChains.ForwardFilter.Clear(); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
|
||||
if err := listChains.ForwardBridge.Clear(); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
|
||||
if err := listChains.ForwardCT.Clear(); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
func (g *generator) ClearChains(builder nft.BatchBuilder, chains firewall.NFTDockerChains) {
|
||||
for _, chain := range chains.List() {
|
||||
if err := chain.Clear(builder); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *generator) AddRule(chainData chain.Data, rule string) {
|
||||
if err := chainData.AddRule(rule); err != nil {
|
||||
func (g *generator) AddRule(builder nft.BatchBuilder, chainDocker chain.Docker, rule string) {
|
||||
if err := chainDocker.AddRule(builder, rule); err != nil {
|
||||
g.logger.Error(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package rule_strategy
|
||||
|
||||
import (
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/client"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/firewall"
|
||||
)
|
||||
|
||||
type Strategy interface {
|
||||
Reload(newNoneChain func(chain string) (nftChain.Chain, error)) error
|
||||
Chains() chain.Chains
|
||||
Reload(nftDocker firewall.NFTDocker) error
|
||||
Chains() firewall.NFTDockerChains
|
||||
Event(event *client.Event)
|
||||
}
|
||||
|
||||
@@ -3,15 +3,16 @@ package rule_strategy
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/chain"
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/client"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/firewall"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
)
|
||||
|
||||
type incrementalStrategy struct {
|
||||
dockerClient client.Docker
|
||||
chains chain.Chains
|
||||
nftDocker firewall.NFTDocker
|
||||
generator Generator
|
||||
logger log.Logger
|
||||
}
|
||||
@@ -24,20 +25,26 @@ func NewIncrementalStrategy(generator Generator, dockerClient client.Docker, log
|
||||
}
|
||||
}
|
||||
|
||||
func (i *incrementalStrategy) Reload(newNoneChain func(chain string) (nftChain.Chain, error)) error {
|
||||
chains, err := chain.NewChains(newNoneChain)
|
||||
func (i *incrementalStrategy) Reload(nftDocker firewall.NFTDocker) error {
|
||||
i.nftDocker = nftDocker
|
||||
|
||||
batchBuilder, err := i.nftDocker.NFT().NewBuildBatch()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.chains = chains
|
||||
defer func() {
|
||||
if err := batchBuilder.Close(); err != nil {
|
||||
i.logger.Warn(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
i.generator.GenerateAll(i.chains, true)
|
||||
i.generator.GenerateAll(batchBuilder, i.nftDocker.Chains(), true)
|
||||
|
||||
return nil
|
||||
return i.nftDocker.NFT().RunBatch(batchBuilder)
|
||||
}
|
||||
|
||||
func (i *incrementalStrategy) Chains() chain.Chains {
|
||||
return i.chains
|
||||
func (i *incrementalStrategy) Chains() firewall.NFTDockerChains {
|
||||
return i.nftDocker.Chains()
|
||||
}
|
||||
|
||||
func (i *incrementalStrategy) Event(event *client.Event) {
|
||||
@@ -54,7 +61,9 @@ func (i *incrementalStrategy) Event(event *client.Event) {
|
||||
}
|
||||
|
||||
if event.Action == "die" {
|
||||
i.eventContainerStop(event.ID)
|
||||
if err := i.eventContainerStop(event.ID); err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to handle container stop event: %s", err))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -66,10 +75,14 @@ func (i *incrementalStrategy) Event(event *client.Event) {
|
||||
if err := i.eventNetworkCreate(event.ID); err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to handle network create event: %s", err))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if event.Action == "destroy" {
|
||||
i.eventNetworkDestroy(event.ID)
|
||||
if err := i.eventNetworkDestroy(event.ID); err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to handle network destroy event: %s", err))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
@@ -82,35 +95,55 @@ func (i *incrementalStrategy) eventContainerStart(containerId string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
batchBuilder, err := i.nftDocker.NFT().NewBuildBatch()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err := batchBuilder.Close(); err != nil {
|
||||
i.logger.Warn(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
for _, ipInfo := range container.Networks.IPAddresses {
|
||||
bridge, err := i.dockerClient.FetchBridge(ipInfo.NetworkID)
|
||||
if err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to fetch bridge for container %s: %s", containerId, err))
|
||||
continue
|
||||
}
|
||||
i.generator.GenerateContainer(container, bridge.Name, i.chains, true)
|
||||
i.generator.GenerateContainer(container, bridge.Name, batchBuilder, i.nftDocker.Chains(), true)
|
||||
}
|
||||
|
||||
return nil
|
||||
return i.nftDocker.NFT().RunBatch(batchBuilder)
|
||||
}
|
||||
|
||||
func (i *incrementalStrategy) eventContainerStop(containerId string) {
|
||||
listChains := i.chains.List()
|
||||
func (i *incrementalStrategy) eventContainerStop(containerId string) error {
|
||||
batchBuilder, err := i.nftDocker.NFT().NewBuildBatch()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err := batchBuilder.Close(); err != nil {
|
||||
i.logger.Warn(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if err := i.nftRuleDeleteContainer(containerId, &listChains.PreroutingFilter); err != nil {
|
||||
if err := i.nftRuleDeleteContainer(containerId, batchBuilder, i.nftDocker.Chains().PreroutingFilter()); err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to delete container %s rules: %s", containerId, err))
|
||||
}
|
||||
|
||||
if err := i.nftRuleDeleteContainer(containerId, &listChains.DockerNat); err != nil {
|
||||
if err := i.nftRuleDeleteContainer(containerId, batchBuilder, i.nftDocker.Chains().DockerNat()); err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to delete container %s rules: %s", containerId, err))
|
||||
}
|
||||
|
||||
if err := i.nftRuleDeleteContainer(containerId, &listChains.DockerFilterFirst); err != nil {
|
||||
if err := i.nftRuleDeleteContainer(containerId, batchBuilder, i.nftDocker.Chains().DockerFilterFirst()); err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to delete container %s rules: %s", containerId, err))
|
||||
}
|
||||
|
||||
return i.nftDocker.NFT().RunBatch(batchBuilder)
|
||||
}
|
||||
|
||||
func (i *incrementalStrategy) nftRuleDeleteContainer(containerId string, chain *chain.Data) error {
|
||||
func (i *incrementalStrategy) nftRuleDeleteContainer(containerId string, builder nft.BatchBuilder, chain chain.Docker) error {
|
||||
rules, err := chain.ListRules()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -120,7 +153,7 @@ func (i *incrementalStrategy) nftRuleDeleteContainer(containerId string, chain *
|
||||
if rule.Comment != "container_id:"+containerId {
|
||||
continue
|
||||
}
|
||||
if err := chain.RemoveRuleByHandle(rule.Handle); err != nil {
|
||||
if err := chain.RemoveRuleByHandle(builder, rule.Handle); err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to delete container %s rule: %s", containerId, err))
|
||||
}
|
||||
}
|
||||
@@ -129,40 +162,60 @@ func (i *incrementalStrategy) nftRuleDeleteContainer(containerId string, chain *
|
||||
}
|
||||
|
||||
func (i *incrementalStrategy) eventNetworkCreate(bridgeId string) error {
|
||||
batchBuilder, err := i.nftDocker.NFT().NewBuildBatch()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err := batchBuilder.Close(); err != nil {
|
||||
i.logger.Warn(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
bridge, err := i.dockerClient.FetchBridge(bridgeId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
i.generator.GenerateBridge(bridge, i.chains, true)
|
||||
return nil
|
||||
i.generator.GenerateBridge(bridge, batchBuilder, i.nftDocker.Chains(), true)
|
||||
return i.nftDocker.NFT().RunBatch(batchBuilder)
|
||||
}
|
||||
|
||||
func (i *incrementalStrategy) eventNetworkDestroy(bridgeId string) {
|
||||
listChains := i.chains.List()
|
||||
func (i *incrementalStrategy) eventNetworkDestroy(bridgeId string) error {
|
||||
batchBuilder, err := i.nftDocker.NFT().NewBuildBatch()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err := batchBuilder.Close(); err != nil {
|
||||
i.logger.Warn(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if err := i.nftRuleDeleteBridge(bridgeId, &listChains.DockerFilterSecond); err != nil {
|
||||
if err := i.nftRuleDeleteBridge(bridgeId, batchBuilder, i.nftDocker.Chains().DockerFilterSecond()); err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to delete bridge %s rules: %s", bridgeId, err))
|
||||
}
|
||||
|
||||
if err := i.nftRuleDeleteBridge(bridgeId, &listChains.ForwardFilter); err != nil {
|
||||
if err := i.nftRuleDeleteBridge(bridgeId, batchBuilder, i.nftDocker.Chains().ForwardFilter()); err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to delete bridge %s rules: %s", bridgeId, err))
|
||||
}
|
||||
|
||||
if err := i.nftRuleDeleteBridge(bridgeId, &listChains.ForwardBridge); err != nil {
|
||||
if err := i.nftRuleDeleteBridge(bridgeId, batchBuilder, i.nftDocker.Chains().ForwardBridge()); err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to delete bridge %s rules: %s", bridgeId, err))
|
||||
}
|
||||
|
||||
if err := i.nftRuleDeleteBridge(bridgeId, &listChains.ForwardCT); err != nil {
|
||||
if err := i.nftRuleDeleteBridge(bridgeId, batchBuilder, i.nftDocker.Chains().ForwardCT()); err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to delete bridge %s rules: %s", bridgeId, err))
|
||||
}
|
||||
|
||||
if err := i.nftRuleDeleteBridge(bridgeId, &listChains.PostroutingNat); err != nil {
|
||||
if err := i.nftRuleDeleteBridge(bridgeId, batchBuilder, i.nftDocker.Chains().PostroutingNat()); err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to delete bridge %s rules: %s", bridgeId, err))
|
||||
}
|
||||
|
||||
return i.nftDocker.NFT().RunBatch(batchBuilder)
|
||||
}
|
||||
|
||||
func (i *incrementalStrategy) nftRuleDeleteBridge(bridgeId string, chain *chain.Data) error {
|
||||
func (i *incrementalStrategy) nftRuleDeleteBridge(bridgeId string, builder nft.BatchBuilder, chain chain.Docker) error {
|
||||
rules, err := chain.ListRules()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -172,7 +225,7 @@ func (i *incrementalStrategy) nftRuleDeleteBridge(bridgeId string, chain *chain.
|
||||
if rule.Comment != "bridge_id:"+bridgeId {
|
||||
continue
|
||||
}
|
||||
if err := chain.RemoveRuleByHandle(rule.Handle); err != nil {
|
||||
if err := chain.RemoveRuleByHandle(builder, rule.Handle); err != nil {
|
||||
i.logger.Error(fmt.Sprintf("failed to delete bridge %s rule: %s", bridgeId, err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,44 @@
|
||||
package rule_strategy
|
||||
|
||||
import (
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/client"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/firewall"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
)
|
||||
|
||||
type rebuildStrategy struct {
|
||||
chains chain.Chains
|
||||
nftDocker firewall.NFTDocker
|
||||
generator Generator
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
func NewRebuildStrategy(generator Generator) Strategy {
|
||||
func NewRebuildStrategy(generator Generator, logger log.Logger) Strategy {
|
||||
return &rebuildStrategy{
|
||||
generator: generator,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *rebuildStrategy) Reload(newNoneChain func(chain string) (nftChain.Chain, error)) error {
|
||||
chains, err := chain.NewChains(newNoneChain)
|
||||
func (r *rebuildStrategy) Reload(nftDocker firewall.NFTDocker) error {
|
||||
r.nftDocker = nftDocker
|
||||
|
||||
batchBuilder, err := r.nftDocker.NFT().NewBuildBatch()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.chains = chains
|
||||
defer func() {
|
||||
if err := batchBuilder.Close(); err != nil {
|
||||
r.logger.Warn(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
r.generator.GenerateAll(r.chains, false)
|
||||
r.generator.GenerateAll(batchBuilder, r.nftDocker.Chains(), false)
|
||||
|
||||
return nil
|
||||
return r.nftDocker.NFT().RunBatch(batchBuilder)
|
||||
}
|
||||
|
||||
func (r *rebuildStrategy) Chains() chain.Chains {
|
||||
return r.chains
|
||||
func (r *rebuildStrategy) Chains() firewall.NFTDockerChains {
|
||||
return r.nftDocker.Chains()
|
||||
}
|
||||
|
||||
func (r *rebuildStrategy) Event(event *client.Event) {
|
||||
@@ -38,6 +46,21 @@ func (r *rebuildStrategy) Event(event *client.Event) {
|
||||
return
|
||||
}
|
||||
|
||||
r.generator.ClearChains(r.chains)
|
||||
r.generator.GenerateAll(r.chains, false)
|
||||
batchBuilder, err := r.nftDocker.NFT().NewBuildBatch()
|
||||
if err != nil {
|
||||
r.logger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err := batchBuilder.Close(); err != nil {
|
||||
r.logger.Warn(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
r.generator.ClearChains(batchBuilder, r.nftDocker.Chains())
|
||||
r.generator.GenerateAll(batchBuilder, r.nftDocker.Chains(), false)
|
||||
|
||||
if err := r.nftDocker.NFT().RunBatch(batchBuilder); err != nil {
|
||||
r.logger.Error(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,13 +9,14 @@ import (
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db/entity"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db/repository"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/chain/block"
|
||||
nftFirewall "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/block"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/types"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
)
|
||||
|
||||
type API interface {
|
||||
NftReload(blockListIP block.ListIP, blockListIPWithPort block.ListIPWithPort) error
|
||||
NftReload(nft nftFirewall.NFT, blockListIP block.ListIP, blockListIPWithPort block.ListIPWithPort) error
|
||||
BlockIP(block BlockIP) (bool, error)
|
||||
BlockIPWithPorts(block BlockIPWithPorts) (bool, error)
|
||||
UnblockAllIPs() error
|
||||
@@ -53,15 +54,25 @@ func New(blockingRepository repository.BlockingRepository, logger log.Logger) AP
|
||||
}
|
||||
}
|
||||
|
||||
func (b *blocking) NftReload(blockListIP block.ListIP, blockListIPWithPort block.ListIPWithPort) error {
|
||||
func (b *blocking) NftReload(nft nftFirewall.NFT, blockListIP block.ListIP, blockListIPWithPort block.ListIPWithPort) error {
|
||||
b.mu.Lock()
|
||||
b.blockListIP = blockListIP
|
||||
b.blockListIPWithPort = blockListIPWithPort
|
||||
b.mu.Unlock()
|
||||
|
||||
batchBuilder, err := nft.NewBuildBatch()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err := batchBuilder.Close(); err != nil {
|
||||
b.logger.Warn(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
isExpiredEntries := false
|
||||
nowUnix := time.Now().Unix()
|
||||
err := b.blockingRepository.List(func(e entity.Blocking) error {
|
||||
err = b.blockingRepository.List(func(e entity.Blocking) error {
|
||||
ip := net.ParseIP(e.IP)
|
||||
if ip == nil {
|
||||
b.logger.Error(fmt.Sprintf("Failed to parse IP address: %s", e.IP))
|
||||
@@ -83,14 +94,14 @@ func (b *blocking) NftReload(blockListIP block.ListIP, blockListIPWithPort block
|
||||
b.logger.Error(fmt.Sprintf("Failed to parse ports: %s", err))
|
||||
return nil
|
||||
}
|
||||
if err := b.blockListIPWithPort.AddIP(ip, l4Ports, blockSeconds); err != nil {
|
||||
if err := b.blockListIPWithPort.AddBatchIP(batchBuilder, ip, l4Ports, blockSeconds); err != nil {
|
||||
b.logger.Error(fmt.Sprintf("Failed to add IP %s to block list: %s", ip.String(), err))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := b.blockListIP.AddIP(ip, blockSeconds); err != nil {
|
||||
if err := b.blockListIP.AddBatchIP(batchBuilder, ip, blockSeconds); err != nil {
|
||||
b.logger.Error(fmt.Sprintf("Failed to add IP %s to block list: %s", ip.String(), err))
|
||||
return nil
|
||||
}
|
||||
@@ -108,7 +119,11 @@ func (b *blocking) NftReload(blockListIP block.ListIP, blockListIPWithPort block
|
||||
}()
|
||||
}
|
||||
|
||||
return err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nft.RunBatch(batchBuilder)
|
||||
}
|
||||
|
||||
func (b *blocking) BlockIP(block BlockIP) (bool, error) {
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
type AfterLocalInput interface {
|
||||
AddRule(expr ...string) error
|
||||
AddRuleIn(AddRuleFunc func(expr ...string) error) error
|
||||
}
|
||||
|
||||
type afterLocalInput struct {
|
||||
nft nft.NFT
|
||||
family family.Type
|
||||
table string
|
||||
chain string
|
||||
}
|
||||
|
||||
func newAfterLocalInput(nft nft.NFT, family family.Type, table string) (LocalInput, error) {
|
||||
chain := "after-local-input"
|
||||
if err := nft.Chain().Add(family, table, chain, nftChain.TypeNone); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &afterLocalInput{
|
||||
nft: nft,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *afterLocalInput) AddRule(expr ...string) error {
|
||||
return l.nft.Rule().Add(l.family, l.table, l.chain, expr...)
|
||||
}
|
||||
|
||||
func (l *afterLocalInput) AddRuleIn(AddRuleFunc func(expr ...string) error) error {
|
||||
return AddRuleFunc("iifname != \"lo\" counter jump " + l.chain)
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
type BeforeLocalInput interface {
|
||||
AddRule(expr ...string) error
|
||||
AddRuleIn(AddRuleFunc func(expr ...string) error) error
|
||||
}
|
||||
|
||||
type beforeLocalInput struct {
|
||||
nft nft.NFT
|
||||
family family.Type
|
||||
table string
|
||||
chain string
|
||||
}
|
||||
|
||||
func newBeforeLocalInput(nft nft.NFT, family family.Type, table string) (LocalInput, error) {
|
||||
chain := "before-local-input"
|
||||
if err := nft.Chain().Add(family, table, chain, nftChain.TypeNone); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &beforeLocalInput{
|
||||
nft: nft,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *beforeLocalInput) AddRule(expr ...string) error {
|
||||
return l.nft.Rule().Add(l.family, l.table, l.chain, expr...)
|
||||
}
|
||||
|
||||
func (l *beforeLocalInput) AddRuleIn(AddRuleFunc func(expr ...string) error) error {
|
||||
return AddRuleFunc("iifname != \"lo\" counter jump " + l.chain)
|
||||
}
|
||||
@@ -1,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
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
type Chain interface {
|
||||
AddRule(expr ...string) error
|
||||
ListRules() ([]Rule, error)
|
||||
RemoveRuleByHandle(handle uint64) error
|
||||
Clear() error
|
||||
}
|
||||
|
||||
type chain struct {
|
||||
nft nft.NFT
|
||||
family family.Type
|
||||
table string
|
||||
chain string
|
||||
}
|
||||
|
||||
type NftOutput struct {
|
||||
Nftables []NftElement `json:"nftables"`
|
||||
}
|
||||
type NftElement struct {
|
||||
Rule *Rule `json:"rule,omitempty"`
|
||||
}
|
||||
|
||||
type Rule struct {
|
||||
Handle uint64 `json:"handle"`
|
||||
Comment string `json:"comment"`
|
||||
}
|
||||
|
||||
func (c *chain) AddRule(expr ...string) error {
|
||||
return c.nft.Rule().Add(c.family, c.table, c.chain, expr...)
|
||||
}
|
||||
|
||||
func (c *chain) ListRules() ([]Rule, error) {
|
||||
args := []string{"-a", "-j", "list", "chain", c.family.String(), c.table, c.chain}
|
||||
jsonData, err := c.nft.Command().RunWithOutput(args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var output NftOutput
|
||||
if err := json.Unmarshal([]byte(jsonData), &output); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var rules []Rule
|
||||
for _, el := range output.Nftables {
|
||||
if el.Rule != nil {
|
||||
rules = append(rules, *el.Rule)
|
||||
}
|
||||
}
|
||||
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
func (c *chain) RemoveRuleByHandle(handle uint64) error {
|
||||
return c.nft.Rule().Delete(c.family, c.table, c.chain, handle)
|
||||
}
|
||||
|
||||
func (c *chain) Clear() error {
|
||||
return c.nft.Chain().Clear(c.family, c.table, c.chain)
|
||||
}
|
||||
@@ -1,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
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
type Forward interface {
|
||||
AddRule(expr ...string) error
|
||||
}
|
||||
|
||||
type forward struct {
|
||||
nft nft.NFT
|
||||
family family.Type
|
||||
table string
|
||||
chain string
|
||||
}
|
||||
|
||||
func newForward(nft nft.NFT, family family.Type, table string, chain string, defaultAllow bool, priority int) (Forward, error) {
|
||||
policy := nftChain.PolicyDrop
|
||||
if defaultAllow {
|
||||
policy = nftChain.PolicyAccept
|
||||
}
|
||||
|
||||
baseChain := nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeFilter,
|
||||
Hook: nftChain.HookForward,
|
||||
Priority: int32(priority),
|
||||
Policy: policy,
|
||||
Device: "",
|
||||
}
|
||||
|
||||
if err := nft.Chain().Add(family, table, chain, baseChain); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &forward{
|
||||
nft: nft,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *forward) AddRule(expr ...string) error {
|
||||
return c.nft.Rule().Add(c.family, c.table, c.chain, expr...)
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
type Input interface {
|
||||
AddRule(expr ...string) error
|
||||
}
|
||||
|
||||
type input struct {
|
||||
nft nft.NFT
|
||||
family family.Type
|
||||
table string
|
||||
chain string
|
||||
}
|
||||
|
||||
func newInput(nft nft.NFT, family family.Type, table string, chain string, defaultAllow bool, priority int) (Input, error) {
|
||||
policy := nftChain.PolicyDrop
|
||||
if defaultAllow {
|
||||
policy = nftChain.PolicyAccept
|
||||
}
|
||||
|
||||
baseChain := nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeFilter,
|
||||
Hook: nftChain.HookInput,
|
||||
Priority: int32(priority),
|
||||
Policy: policy,
|
||||
Device: "",
|
||||
}
|
||||
|
||||
if err := nft.Chain().Add(family, table, chain, baseChain); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &input{
|
||||
nft: nft,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *input) AddRule(expr ...string) error {
|
||||
return c.nft.Rule().Add(c.family, c.table, c.chain, expr...)
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
type LocalForward interface {
|
||||
AddRule(expr ...string) error
|
||||
AddRuleIn(AddRuleFunc func(expr ...string) error) error
|
||||
}
|
||||
|
||||
type localForward struct {
|
||||
nft nft.NFT
|
||||
family family.Type
|
||||
table string
|
||||
chain string
|
||||
}
|
||||
|
||||
func newLocalForward(nft nft.NFT, family family.Type, table string) (LocalForward, error) {
|
||||
chain := "local-forward"
|
||||
if err := nft.Chain().Add(family, table, chain, nftChain.TypeNone); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &localForward{
|
||||
nft: nft,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *localForward) AddRule(expr ...string) error {
|
||||
return l.nft.Rule().Add(l.family, l.table, l.chain, expr...)
|
||||
}
|
||||
|
||||
func (l *localForward) AddRuleIn(AddRuleFunc func(expr ...string) error) error {
|
||||
return AddRuleFunc("iifname != \"lo\" counter jump " + l.chain)
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
type LocalInput interface {
|
||||
AddRule(expr ...string) error
|
||||
AddRuleIn(AddRuleFunc func(expr ...string) error) error
|
||||
}
|
||||
|
||||
type localInput struct {
|
||||
nft nft.NFT
|
||||
family family.Type
|
||||
table string
|
||||
chain string
|
||||
}
|
||||
|
||||
func newLocalInput(nft nft.NFT, family family.Type, table string) (LocalInput, error) {
|
||||
chain := "local-input"
|
||||
if err := nft.Chain().Add(family, table, chain, nftChain.TypeNone); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &localInput{
|
||||
nft: nft,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *localInput) AddRule(expr ...string) error {
|
||||
return l.nft.Rule().Add(l.family, l.table, l.chain, expr...)
|
||||
}
|
||||
|
||||
func (l *localInput) AddRuleIn(AddRuleFunc func(expr ...string) error) error {
|
||||
return AddRuleFunc("iifname != \"lo\" counter jump " + l.chain)
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
type LocalOutput interface {
|
||||
AddRule(expr ...string) error
|
||||
AddRuleOut(AddRuleFunc func(expr ...string) error) error
|
||||
}
|
||||
|
||||
type localOutput struct {
|
||||
nft nft.NFT
|
||||
family family.Type
|
||||
table string
|
||||
chain string
|
||||
}
|
||||
|
||||
func newLocalOutput(nft nft.NFT, family family.Type, table string) (LocalOutput, error) {
|
||||
chain := "local-output"
|
||||
if err := nft.Chain().Add(family, table, chain, nftChain.TypeNone); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &localOutput{
|
||||
nft: nft,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *localOutput) AddRule(expr ...string) error {
|
||||
return l.nft.Rule().Add(l.family, l.table, l.chain, expr...)
|
||||
}
|
||||
|
||||
func (l *localOutput) AddRuleOut(AddRuleFunc func(expr ...string) error) error {
|
||||
return AddRuleFunc("oifname != \"lo\" counter jump " + l.chain)
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
type Output interface {
|
||||
AddRule(expr ...string) error
|
||||
}
|
||||
|
||||
type output struct {
|
||||
nft nft.NFT
|
||||
family family.Type
|
||||
table string
|
||||
chain string
|
||||
}
|
||||
|
||||
func newOutput(nft nft.NFT, family family.Type, table string, chain string, defaultAllow bool, priority int) (Output, error) {
|
||||
policy := nftChain.PolicyDrop
|
||||
if defaultAllow {
|
||||
policy = nftChain.PolicyAccept
|
||||
}
|
||||
|
||||
baseChain := nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeFilter,
|
||||
Hook: nftChain.HookOutput,
|
||||
Priority: int32(priority),
|
||||
Policy: policy,
|
||||
Device: "",
|
||||
}
|
||||
|
||||
if err := nft.Chain().Add(family, table, chain, baseChain); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &output{
|
||||
nft: nft,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *output) AddRule(expr ...string) error {
|
||||
return c.nft.Rule().Add(c.family, c.table, c.chain, expr...)
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
nftFamily "git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
type PacketFilter interface {
|
||||
AddRuleIn(AddRuleFunc func(expr ...string) error) error
|
||||
AddRuleOut(AddRuleFunc func(expr ...string) error) error
|
||||
}
|
||||
|
||||
type packetFilter struct {
|
||||
enable bool
|
||||
invalidName string
|
||||
}
|
||||
|
||||
// newPacketFilter Drop out of order packets and packets in an INVALID state in nftables connection tracking.
|
||||
func newPacketFilter(nft nft.NFT, family nftFamily.Type, table string, enable bool) (PacketFilter, error) {
|
||||
chainInvalidName := "INVALID"
|
||||
if !enable {
|
||||
return &packetFilter{
|
||||
enable: enable,
|
||||
invalidName: chainInvalidName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
chainName := "INVDROP"
|
||||
|
||||
if err := nft.Chain().Add(family, table, chainName, nftChain.TypeNone); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := nft.Rule().Add(family, table, chainName, "counter drop"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := nft.Chain().Add(family, table, chainInvalidName, nftChain.TypeNone); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := nft.Rule().Add(family, table, chainInvalidName, "ct state invalid counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := nft.Rule().Add(family, table, chainInvalidName, "tcp flags ! fin,syn,rst,psh,ack,urg counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := nft.Rule().Add(family, table, chainInvalidName, "tcp flags & (fin | syn | rst | psh | ack | urg) == fin | syn | rst | psh | ack | urg counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := nft.Rule().Add(family, table, chainInvalidName, "tcp flags & (fin | syn) == fin | syn counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := nft.Rule().Add(family, table, chainInvalidName, "tcp flags & (syn | rst) == syn | rst counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := nft.Rule().Add(family, table, chainInvalidName, "tcp flags & (fin | rst) == fin | rst counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := nft.Rule().Add(family, table, chainInvalidName, "tcp flags & (fin | ack) == fin counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := nft.Rule().Add(family, table, chainInvalidName, "tcp flags & (psh | ack) == psh counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := nft.Rule().Add(family, table, chainInvalidName, "tcp flags & (ack | urg) == urg counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := nft.Rule().Add(family, table, chainInvalidName, "tcp flags & (fin | syn | rst | ack) != syn ct state new counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &packetFilter{
|
||||
enable: enable,
|
||||
invalidName: chainInvalidName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *packetFilter) AddRuleIn(AddRuleFunc func(expr ...string) error) error {
|
||||
if !f.enable {
|
||||
return nil
|
||||
}
|
||||
return AddRuleFunc("iifname != \"lo\" meta l4proto tcp counter jump " + f.invalidName)
|
||||
}
|
||||
|
||||
func (f *packetFilter) AddRuleOut(AddRuleFunc func(expr ...string) error) error {
|
||||
if !f.enable {
|
||||
return nil
|
||||
}
|
||||
return AddRuleFunc("oifname != \"lo\" meta l4proto tcp counter jump " + f.invalidName)
|
||||
}
|
||||
@@ -1,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
|
||||
@@ -1,72 +0,0 @@
|
||||
package firewall
|
||||
|
||||
import nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
|
||||
func (f *firewall) reloadDocker() error {
|
||||
f.logger.Debug("Reload docker rules")
|
||||
if err := f.reloadDockerPrerouting(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *firewall) reloadDockerPrerouting() error {
|
||||
preroutingNat, err := f.chains.NewChain("prerouting_nat", nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeNat,
|
||||
Hook: nftChain.HookPrerouting,
|
||||
Priority: -100,
|
||||
Policy: nftChain.PolicyAccept,
|
||||
Device: "",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.docker.NftChains().PreroutingNatJump(preroutingNat.AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
preroutingFilter, err := f.chains.NewChain("prerouting_filter", nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeFilter,
|
||||
Hook: nftChain.HookPrerouting,
|
||||
Priority: -300,
|
||||
Policy: nftChain.PolicyAccept,
|
||||
Device: "",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.docker.NftChains().PreroutingFilterJump(preroutingFilter.AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
outputNat, err := f.chains.NewChain("output_nat", nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeNat,
|
||||
Hook: nftChain.HookOutput,
|
||||
Priority: -100,
|
||||
Policy: nftChain.PolicyAccept,
|
||||
Device: "",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.docker.NftChains().OutputNatJump(outputNat.AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
postroutingNat, err := f.chains.NewChain("postrouting_nat", nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeNat,
|
||||
Hook: nftChain.HookPostrouting,
|
||||
Priority: 300,
|
||||
Policy: nftChain.PolicyAccept,
|
||||
Device: "",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.docker.NftChains().PostroutingNatJump(postroutingNat.AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -4,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
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package block
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
nftFirewall "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft"
|
||||
)
|
||||
|
||||
type List interface {
|
||||
Name() string
|
||||
AddElement(element string) error
|
||||
AddBatchElement(builder nft.BatchBuilder, element string) error
|
||||
DeleteElement(element string) error
|
||||
ReplaceElements(elements []string) error
|
||||
ReplaceBatchElements(builder nft.BatchBuilder, elements []string) error
|
||||
}
|
||||
|
||||
type list struct {
|
||||
nft nftFirewall.NFT
|
||||
family family.Type
|
||||
table string
|
||||
name string
|
||||
}
|
||||
|
||||
func newList(nft nftFirewall.NFT, builder nft.BatchBuilder, family family.Type, table string, name string, params string) (List, error) {
|
||||
command := []string{
|
||||
"add set", family.String(), table, name, "{ " + params + " }",
|
||||
}
|
||||
if err := builder.Command().Run(command...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &list{
|
||||
nft: nft,
|
||||
family: family,
|
||||
table: table,
|
||||
name: name,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func newListWithoutCommand(nft nftFirewall.NFT, family family.Type, table string, name string) List {
|
||||
return &list{
|
||||
nft: nft,
|
||||
family: family,
|
||||
table: table,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *list) Name() string {
|
||||
return l.name
|
||||
}
|
||||
|
||||
func (l *list) AddElement(element string) error {
|
||||
command := []string{
|
||||
"add element",
|
||||
l.family.String(), l.table, l.name,
|
||||
fmt.Sprintf("{ %s }", element),
|
||||
}
|
||||
return l.nft.NFT().Command().Run(command...)
|
||||
}
|
||||
|
||||
func (l *list) AddBatchElement(builder nft.BatchBuilder, element string) error {
|
||||
command := []string{
|
||||
"add element",
|
||||
l.family.String(), l.table, l.name,
|
||||
fmt.Sprintf("{ %s }", element),
|
||||
}
|
||||
return builder.Command().Run(command...)
|
||||
}
|
||||
|
||||
func (l *list) DeleteElement(element string) error {
|
||||
command := []string{
|
||||
"delete element",
|
||||
l.family.String(), l.table, l.name,
|
||||
fmt.Sprintf("{ %s }", element),
|
||||
}
|
||||
return l.nft.NFT().Command().Run(command...)
|
||||
}
|
||||
|
||||
func (l *list) ReplaceElements(elements []string) error {
|
||||
batchBuilder, err := l.nft.NewBuildBatch()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = batchBuilder.Close()
|
||||
}()
|
||||
|
||||
if err := l.replaceElements(batchBuilder, elements); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return l.nft.RunBatch(batchBuilder)
|
||||
}
|
||||
|
||||
func (l *list) ReplaceBatchElements(builder nft.BatchBuilder, elements []string) error {
|
||||
return l.replaceElements(builder, elements)
|
||||
}
|
||||
|
||||
func (l *list) replaceElements(builder nft.BatchBuilder, elements []string) error {
|
||||
if err := builder.Command().Run("flush set", l.family.String(), l.table, l.name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(elements) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
command := []string{
|
||||
"add element",
|
||||
l.family.String(), l.table, l.name,
|
||||
fmt.Sprintf("{ %s }", strings.Join(elements, ",")),
|
||||
}
|
||||
|
||||
return builder.Command().Run(command...)
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package block
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
nftFirewall "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/rule"
|
||||
)
|
||||
|
||||
type ListIP interface {
|
||||
// AddIP Add an IP address to the list.
|
||||
AddIP(addr net.IP, banSeconds uint32) error
|
||||
|
||||
// AddBatchIP Add an IP address to the list.
|
||||
AddBatchIP(builder nft.BatchBuilder, addr net.IP, banSeconds uint32) error
|
||||
|
||||
// DeleteIP Delete an IP address from the list.
|
||||
DeleteIP(addr net.IP) error
|
||||
|
||||
// AddRuleToChain Add a rule to the parent chain.
|
||||
AddRuleToChain(chainAddRuleFunc rule.AddFunc, action string) error
|
||||
}
|
||||
|
||||
type listIP struct {
|
||||
listIPv4 List
|
||||
listIPv6 List
|
||||
}
|
||||
|
||||
func NewListIP(nft nftFirewall.NFT, builder nft.BatchBuilder, family family.Type, table string, name string) (ListIP, error) {
|
||||
listNameV4, listNameV6 := getNamesIP(name)
|
||||
|
||||
params := "type ipv4_addr; flags interval, timeout;"
|
||||
listIPv4, err := newList(nft, builder, family, table, listNameV4, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params = "type ipv6_addr; flags interval, timeout;"
|
||||
listIPv6, err := newList(nft, builder, family, table, listNameV6, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &listIP{
|
||||
listIPv4: listIPv4,
|
||||
listIPv6: listIPv6,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewListIPWithoutCommand(nft nftFirewall.NFT, family family.Type, table string, name string) ListIP {
|
||||
listNameV4, listNameV6 := getNamesIP(name)
|
||||
|
||||
listIPv4 := newListWithoutCommand(nft, family, table, listNameV4)
|
||||
listIPv6 := newListWithoutCommand(nft, family, table, listNameV6)
|
||||
|
||||
return &listIP{
|
||||
listIPv4: listIPv4,
|
||||
listIPv6: listIPv6,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *listIP) AddIP(addr net.IP, banSeconds uint32) error {
|
||||
el := []string{addr.String()}
|
||||
if banSeconds > 0 {
|
||||
el = append(el, "timeout", fmt.Sprintf("%ds", banSeconds))
|
||||
}
|
||||
|
||||
element := strings.Join(el, " ")
|
||||
|
||||
if addr.To4() != nil {
|
||||
return l.listIPv4.AddElement(element)
|
||||
}
|
||||
|
||||
return l.listIPv6.AddElement(element)
|
||||
}
|
||||
|
||||
func (l *listIP) AddBatchIP(builder nft.BatchBuilder, addr net.IP, banSeconds uint32) error {
|
||||
el := []string{addr.String()}
|
||||
if banSeconds > 0 {
|
||||
el = append(el, "timeout", fmt.Sprintf("%ds", banSeconds))
|
||||
}
|
||||
|
||||
element := strings.Join(el, " ")
|
||||
|
||||
if addr.To4() != nil {
|
||||
return l.listIPv4.AddBatchElement(builder, element)
|
||||
}
|
||||
|
||||
return l.listIPv6.AddBatchElement(builder, element)
|
||||
}
|
||||
|
||||
func (l *listIP) DeleteIP(addr net.IP) error {
|
||||
if addr == nil {
|
||||
return fmt.Errorf("IP address cannot be nil")
|
||||
}
|
||||
if addr.To4() != nil {
|
||||
return l.listIPv4.DeleteElement(addr.String())
|
||||
}
|
||||
|
||||
return l.listIPv6.DeleteElement(addr.String())
|
||||
}
|
||||
|
||||
func (l *listIP) AddRuleToChain(chainAddRuleFunc rule.AddFunc, action string) error {
|
||||
rule := "ip saddr @" + l.listIPv4.Name() + " " + action
|
||||
if err := chainAddRuleFunc(rule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rule = "ip6 saddr @" + l.listIPv6.Name() + " " + action
|
||||
if err := chainAddRuleFunc(rule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+48
-8
@@ -5,8 +5,10 @@ import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
nftFirewall "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/rule"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/types"
|
||||
)
|
||||
|
||||
@@ -14,11 +16,14 @@ type ListIPWithPort interface {
|
||||
// AddIP Add an IP address to the list.
|
||||
AddIP(addr net.IP, ports []types.L4Port, banSeconds uint32) error
|
||||
|
||||
// AddBatchIP Add an IP address to the list.
|
||||
AddBatchIP(builder nft.BatchBuilder, addr net.IP, ports []types.L4Port, banSeconds uint32) error
|
||||
|
||||
// DeleteIP Delete an IP address from the list.
|
||||
DeleteIP(addr net.IP, port types.L4Port) error
|
||||
|
||||
// AddRuleToChain Add a rule to the parent chain.
|
||||
AddRuleToChain(chainAddRuleFunc func(expr ...string) error, action string) error
|
||||
AddRuleToChain(chainAddRuleFunc rule.AddFunc, action string) error
|
||||
}
|
||||
|
||||
type listIPWithPort struct {
|
||||
@@ -26,17 +31,17 @@ type listIPWithPort struct {
|
||||
listIPv6 List
|
||||
}
|
||||
|
||||
func NewListIPWithPort(nft nft.NFT, family family.Type, table string, name string) (ListIPWithPort, error) {
|
||||
func NewListIPWithPort(nft nftFirewall.NFT, builder nft.BatchBuilder, family family.Type, table string, name string) (ListIPWithPort, error) {
|
||||
listNameV4, listNameV6 := getNamesIP(name)
|
||||
|
||||
params := "type ipv4_addr . inet_proto . inet_service; flags interval, timeout;"
|
||||
listName := name + "_ip4"
|
||||
listIPv4, err := newList(nft, family, table, listName, params)
|
||||
listIPv4, err := newList(nft, builder, family, table, listNameV4, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params = "type ipv6_addr . inet_proto . inet_service; flags interval, timeout;"
|
||||
listName = name + "_ip6"
|
||||
listIPv6, err := newList(nft, family, table, listName, params)
|
||||
listIPv6, err := newList(nft, builder, family, table, listNameV6, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -47,6 +52,18 @@ func NewListIPWithPort(nft nft.NFT, family family.Type, table string, name strin
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewListIPWithPortWithoutCommand(nft nftFirewall.NFT, family family.Type, table string, name string) ListIPWithPort {
|
||||
listNameV4, listNameV6 := getNamesIP(name)
|
||||
|
||||
listIPv4 := newListWithoutCommand(nft, family, table, listNameV4)
|
||||
listIPv6 := newListWithoutCommand(nft, family, table, listNameV6)
|
||||
|
||||
return &listIPWithPort{
|
||||
listIPv4: listIPv4,
|
||||
listIPv6: listIPv6,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *listIPWithPort) AddIP(addr net.IP, ports []types.L4Port, banSeconds uint32) error {
|
||||
if len(ports) == 0 {
|
||||
return fmt.Errorf("ports is empty")
|
||||
@@ -70,6 +87,29 @@ func (l *listIPWithPort) AddIP(addr net.IP, ports []types.L4Port, banSeconds uin
|
||||
return l.listIPv6.AddElement(element)
|
||||
}
|
||||
|
||||
func (l *listIPWithPort) AddBatchIP(builder nft.BatchBuilder, addr net.IP, ports []types.L4Port, banSeconds uint32) error {
|
||||
if len(ports) == 0 {
|
||||
return fmt.Errorf("ports is empty")
|
||||
}
|
||||
|
||||
var elements []string
|
||||
for _, port := range ports {
|
||||
el := []string{fmt.Sprintf("%s . %s . %d", addr.String(), port.ProtocolString(), port.Number())}
|
||||
if banSeconds > 0 {
|
||||
el = append(el, "timeout", fmt.Sprintf("%ds", banSeconds))
|
||||
}
|
||||
|
||||
elements = append(elements, strings.Join(el, " "))
|
||||
}
|
||||
|
||||
element := strings.Join(elements, ",")
|
||||
if addr.To4() != nil {
|
||||
return l.listIPv4.AddBatchElement(builder, element)
|
||||
}
|
||||
|
||||
return l.listIPv6.AddBatchElement(builder, element)
|
||||
}
|
||||
|
||||
func (l *listIPWithPort) DeleteIP(addr net.IP, port types.L4Port) error {
|
||||
if addr == nil {
|
||||
return fmt.Errorf("IP address cannot be nil")
|
||||
@@ -87,7 +127,7 @@ func (l *listIPWithPort) DeleteIP(addr net.IP, port types.L4Port) error {
|
||||
return l.listIPv6.DeleteElement(element)
|
||||
}
|
||||
|
||||
func (l *listIPWithPort) AddRuleToChain(chainAddRuleFunc func(expr ...string) error, action string) error {
|
||||
func (l *listIPWithPort) AddRuleToChain(chainAddRuleFunc rule.AddFunc, action string) error {
|
||||
rule := "ip saddr . meta l4proto . th dport @" + l.listIPv4.Name() + " " + action
|
||||
if err := chainAddRuleFunc(rule); err != nil {
|
||||
return err
|
||||
@@ -0,0 +1,59 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/rule"
|
||||
)
|
||||
|
||||
type Chain interface {
|
||||
AddRule(expr ...string) error
|
||||
AddRuleIn(AddRuleFunc rule.AddFunc) error
|
||||
AddRuleOut(AddRuleFunc rule.AddFunc) error
|
||||
}
|
||||
|
||||
type batchChain struct {
|
||||
builder nft.BatchBuilder
|
||||
family family.Type
|
||||
table string
|
||||
chain string
|
||||
}
|
||||
|
||||
func NewBatchChain(builder nft.BatchBuilder, family family.Type, table string, chain string) (Chain, error) {
|
||||
if err := builder.Chain().Add(family, table, chain, nftChain.TypeNone); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &batchChain{
|
||||
builder: builder,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewBatchChainWithOptions(builder nft.BatchBuilder, family family.Type, table string, chain string, baseChain nftChain.ChainOptions) (Chain, error) {
|
||||
if err := builder.Chain().Add(family, table, chain, baseChain); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &batchChain{
|
||||
builder: builder,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *batchChain) AddRule(expr ...string) error {
|
||||
return b.builder.Rule().Add(b.family, b.table, b.chain, expr...)
|
||||
}
|
||||
|
||||
func (b *batchChain) AddRuleIn(AddRuleFunc rule.AddFunc) error {
|
||||
return AddRuleFunc("iifname != \"lo\" counter jump " + b.chain)
|
||||
}
|
||||
|
||||
func (b *batchChain) AddRuleOut(AddRuleFunc rule.AddFunc) error {
|
||||
return AddRuleFunc("oifname != \"lo\" counter jump " + b.chain)
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/data"
|
||||
)
|
||||
|
||||
type Docker interface {
|
||||
Name() string
|
||||
AddRule(builder nft.BatchBuilder, expr ...string) error
|
||||
JumpTo(builder nft.BatchBuilder, chain Docker, rule string, comment string) error
|
||||
ListRules() ([]data.Rule, error)
|
||||
RemoveRuleByHandle(builder nft.BatchBuilder, handle uint64) error
|
||||
Clear(builder nft.BatchBuilder) error
|
||||
}
|
||||
|
||||
type docker struct {
|
||||
nft nft.NFT
|
||||
family family.Type
|
||||
table string
|
||||
chain string
|
||||
}
|
||||
|
||||
func NewDocker(nft nft.NFT, family family.Type, table, chain string) Docker {
|
||||
return &docker{
|
||||
nft: nft,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *docker) Name() string {
|
||||
return d.chain
|
||||
}
|
||||
|
||||
func (d *docker) AddRule(builder nft.BatchBuilder, expr ...string) error {
|
||||
return builder.Rule().Add(d.family, d.table, d.chain, expr...)
|
||||
}
|
||||
|
||||
func (d *docker) JumpTo(builder nft.BatchBuilder, chain Docker, rule string, comment string) error {
|
||||
args := []string{rule, "jump", d.chain, comment}
|
||||
return chain.AddRule(builder, args...)
|
||||
}
|
||||
|
||||
func (d *docker) ListRules() ([]data.Rule, error) {
|
||||
args := []string{"-a", "-j", "list", "chain", d.family.String(), d.table, d.chain}
|
||||
jsonData, err := d.nft.Command().RunWithOutput(args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var output data.NftOutput
|
||||
if err := json.Unmarshal([]byte(jsonData), &output); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var rules []data.Rule
|
||||
for _, el := range output.Nftables {
|
||||
if el.Rule != nil {
|
||||
rules = append(rules, *el.Rule)
|
||||
}
|
||||
}
|
||||
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
func (d *docker) RemoveRuleByHandle(builder nft.BatchBuilder, handle uint64) error {
|
||||
return builder.Rule().Delete(d.family, d.table, d.chain, handle)
|
||||
}
|
||||
|
||||
func (d *docker) Clear(builder nft.BatchBuilder) error {
|
||||
return builder.Chain().Clear(d.family, d.table, d.chain)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
func NewBatchForward(builder nft.BatchBuilder, family family.Type, table string, chain string, defaultAllow bool, priority int) (Chain, error) {
|
||||
policy := nftChain.PolicyDrop
|
||||
if defaultAllow {
|
||||
policy = nftChain.PolicyAccept
|
||||
}
|
||||
|
||||
baseChain := nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeFilter,
|
||||
Hook: nftChain.HookForward,
|
||||
Priority: int32(priority),
|
||||
Policy: policy,
|
||||
Device: "",
|
||||
}
|
||||
|
||||
if err := builder.Chain().Add(family, table, chain, baseChain); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &batchChain{
|
||||
builder: builder,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
func NewBatchInput(builder nft.BatchBuilder, family family.Type, table string, chain string, defaultAllow bool, priority int) (Chain, error) {
|
||||
policy := nftChain.PolicyDrop
|
||||
if defaultAllow {
|
||||
policy = nftChain.PolicyAccept
|
||||
}
|
||||
|
||||
baseChain := nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeFilter,
|
||||
Hook: nftChain.HookInput,
|
||||
Priority: int32(priority),
|
||||
Policy: policy,
|
||||
Device: "",
|
||||
}
|
||||
|
||||
if err := builder.Chain().Add(family, table, chain, baseChain); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &batchChain{
|
||||
builder: builder,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
)
|
||||
|
||||
func NewBatchOutput(builder nft.BatchBuilder, family family.Type, table string, chain string, defaultAllow bool, priority int) (Chain, error) {
|
||||
policy := nftChain.PolicyDrop
|
||||
if defaultAllow {
|
||||
policy = nftChain.PolicyAccept
|
||||
}
|
||||
|
||||
baseChain := nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeFilter,
|
||||
Hook: nftChain.HookOutput,
|
||||
Priority: int32(priority),
|
||||
Policy: policy,
|
||||
Device: "",
|
||||
}
|
||||
|
||||
if err := builder.Chain().Add(family, table, chain, baseChain); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &batchChain{
|
||||
builder: builder,
|
||||
family: family,
|
||||
table: table,
|
||||
chain: chain,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package chain
|
||||
|
||||
import "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/rule"
|
||||
|
||||
type PacketFilter interface {
|
||||
AddRuleIn(AddRuleFunc rule.AddFunc) error
|
||||
AddRuleOut(AddRuleFunc rule.AddFunc) error
|
||||
}
|
||||
|
||||
type packetFilter struct {
|
||||
chainName string
|
||||
}
|
||||
|
||||
// NewPacketFilter drop-out-of-order packets and packets in an INVALID state in nftables connection tracking.
|
||||
func NewPacketFilter(chainName string) PacketFilter {
|
||||
return &packetFilter{
|
||||
chainName: chainName,
|
||||
}
|
||||
}
|
||||
|
||||
func (pf *packetFilter) AddRuleIn(addRuleFunc rule.AddFunc) error {
|
||||
return addRuleFunc("iifname != \"lo\" meta l4proto tcp counter jump " + pf.chainName)
|
||||
}
|
||||
|
||||
func (pf *packetFilter) AddRuleOut(addRuleFunc rule.AddFunc) error {
|
||||
return addRuleFunc("oifname != \"lo\" meta l4proto tcp counter jump " + pf.chainName)
|
||||
}
|
||||
|
||||
type packetFilterFalse struct{}
|
||||
|
||||
// NewPacketFilterFalse returns a PacketFilter that does nothing.
|
||||
func NewPacketFilterFalse() PacketFilter {
|
||||
return &packetFilterFalse{}
|
||||
}
|
||||
|
||||
func (pf *packetFilterFalse) AddRuleIn(_ rule.AddFunc) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pf *packetFilterFalse) AddRuleOut(_ rule.AddFunc) error {
|
||||
return nil
|
||||
}
|
||||
+23
-20
@@ -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, " "))
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package data
|
||||
|
||||
type NftOutput struct {
|
||||
Nftables []NftElement `json:"nftables"`
|
||||
}
|
||||
type NftElement struct {
|
||||
Rule *Rule `json:"rule,omitempty"`
|
||||
}
|
||||
|
||||
type Rule struct {
|
||||
Handle uint64 `json:"handle"`
|
||||
Comment string `json:"comment"`
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package nft
|
||||
|
||||
import (
|
||||
nftables "git.kor-elf.net/kor-elf-shield/go-nftables-client"
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
)
|
||||
|
||||
type NFT interface {
|
||||
NewBuildBatch() (nft.BatchBuilder, error)
|
||||
RunBatch(batchBuilder nft.BatchBuilder) error
|
||||
RunBatchAndMoveFile(batchBuilder nft.BatchBuilder, fileDir string) error
|
||||
NFT() nft.NFT
|
||||
}
|
||||
|
||||
type nftImpl struct {
|
||||
nft nft.NFT
|
||||
tmpDir string
|
||||
}
|
||||
|
||||
func New(nft nft.NFT, tmpDir string) NFT {
|
||||
return &nftImpl{
|
||||
nft: nft,
|
||||
tmpDir: tmpDir,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *nftImpl) NFT() nft.NFT {
|
||||
return n.nft
|
||||
}
|
||||
|
||||
func (n *nftImpl) NewBuildBatch() (nft.BatchBuilder, error) {
|
||||
return nftables.NewBatchBuilder(n.tmpDir)
|
||||
}
|
||||
|
||||
func (n *nftImpl) RunBatch(batchBuilder nft.BatchBuilder) error {
|
||||
batch := batchBuilder.Build()
|
||||
defer func() {
|
||||
_ = batch.Close()
|
||||
}()
|
||||
|
||||
return n.nft.ExecuteBatchAfterCheck(batch)
|
||||
}
|
||||
|
||||
func (n *nftImpl) RunBatchAndMoveFile(batchBuilder nft.BatchBuilder, fileDir string) error {
|
||||
batch := batchBuilder.Build()
|
||||
defer func() {
|
||||
_ = batch.Close()
|
||||
}()
|
||||
if err := n.nft.ExecuteBatchAfterCheck(batch); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return batch.MoveFile(fileDir)
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package rule
|
||||
|
||||
import "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/config"
|
||||
|
||||
type AddFunc func(expr ...string) error
|
||||
|
||||
func InputAddIP(addRuleFunc AddFunc, config config.ConfigIP, ipMatch string) error {
|
||||
rule := ipMatch + " saddr " + config.IP + " iifname != \"lo\""
|
||||
if !config.OnlyIP {
|
||||
rule += " " + config.Port.ProtocolString() + " dport " + config.Port.NumberString()
|
||||
}
|
||||
if config.LimitRate != "" {
|
||||
rule += " limit rate " + config.LimitRate
|
||||
}
|
||||
rule += " counter " + config.Action.String()
|
||||
return addRuleFunc(rule)
|
||||
}
|
||||
|
||||
func OutputAddIP(addRuleFunc AddFunc, config config.ConfigIP, ipMatch string) error {
|
||||
rule := ipMatch + " daddr " + config.IP + " oifname != \"lo\""
|
||||
if !config.OnlyIP {
|
||||
rule += " " + config.Port.ProtocolString() + " dport " + config.Port.NumberString()
|
||||
}
|
||||
if config.LimitRate != "" {
|
||||
rule += " limit rate " + config.LimitRate
|
||||
}
|
||||
rule += " counter " + config.Action.String()
|
||||
return addRuleFunc(rule)
|
||||
}
|
||||
|
||||
func ForwardAddIP(addRuleFunc AddFunc, config config.ConfigIP, ipMatch string) error {
|
||||
rule := ipMatch + " saddr " + config.IP + " iifname != \"lo\""
|
||||
|
||||
// There, during routing, the port changes and then the IP blocking rule will not work.
|
||||
//if !config.OnlyIP {
|
||||
// rule += " " + config.Protocol.String() + " dport " + strconv.Itoa(int(config.Port))
|
||||
//}
|
||||
|
||||
rule += " counter " + config.Action.String()
|
||||
if err := addRuleFunc(rule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package table
|
||||
|
||||
import (
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/firewall"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/block"
|
||||
)
|
||||
|
||||
type Table interface {
|
||||
Clear() error
|
||||
DockerChains() firewall.NFTDockerChains
|
||||
BlockList() BlockList
|
||||
}
|
||||
|
||||
type BlockList interface {
|
||||
ListIP() block.ListIP
|
||||
ListIPWithPort() block.ListIPWithPort
|
||||
Blocks() map[string]block.Blocklist
|
||||
}
|
||||
|
||||
type table struct {
|
||||
nft nft.NFT
|
||||
family family.Type
|
||||
name string
|
||||
|
||||
dockerChains firewall.NFTDockerChains
|
||||
blockList BlockList
|
||||
}
|
||||
|
||||
func New(
|
||||
nft nft.NFT, family family.Type, name string,
|
||||
blockList BlockList, dockerChains firewall.NFTDockerChains,
|
||||
) Table {
|
||||
return &table{
|
||||
nft: nft,
|
||||
family: family,
|
||||
name: name,
|
||||
|
||||
dockerChains: dockerChains,
|
||||
blockList: blockList,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *table) Clear() error {
|
||||
// clear does not clean completely
|
||||
return t.nft.NFT().Table().Delete(t.family, t.name)
|
||||
}
|
||||
|
||||
func (t *table) DockerChains() firewall.NFTDockerChains {
|
||||
return t.dockerChains
|
||||
}
|
||||
|
||||
func (t *table) BlockList() BlockList {
|
||||
return t.blockList
|
||||
}
|
||||
|
||||
type blockList struct {
|
||||
listIP block.ListIP
|
||||
listIPWithPort block.ListIPWithPort
|
||||
blocks map[string]block.Blocklist
|
||||
}
|
||||
|
||||
func NewBlockList(listIP block.ListIP, listIPWithPort block.ListIPWithPort, blocks map[string]block.Blocklist) BlockList {
|
||||
return &blockList{
|
||||
listIP: listIP,
|
||||
listIPWithPort: listIPWithPort,
|
||||
blocks: blocks,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *blockList) ListIP() block.ListIP {
|
||||
return b.listIP
|
||||
}
|
||||
|
||||
func (b *blockList) ListIPWithPort() block.ListIPWithPort {
|
||||
return b.listIPWithPort
|
||||
}
|
||||
|
||||
func (b *blockList) Blocks() map[string]block.Blocklist {
|
||||
return b.blocks
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package reload
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/block"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/chain"
|
||||
nftTable "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/table"
|
||||
)
|
||||
|
||||
func (r *reload) blockList(builder nft.BatchBuilder, beforeLocalInput chain.Chain, blocks map[string]block.Blocklist) (nftTable.BlockList, error) {
|
||||
listBlockedIP, err := block.NewListIP(r.nft, builder, r.table.family, r.table.name, blockedIP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := listBlockedIP.AddRuleToChain(beforeLocalInput.AddRule, "drop"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
listBlockedIPWithPort, err := block.NewListIPWithPort(r.nft, builder, r.table.family, r.table.name, blockedIPWithPort)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := listBlockedIPWithPort.AddRuleToChain(beforeLocalInput.AddRule, "drop"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nftTable.NewBlockList(listBlockedIP, listBlockedIPWithPort, blocks), nil
|
||||
}
|
||||
|
||||
func (r *reload) moduleBlockList(builder nft.BatchBuilder, afterLocalInput chain.Chain, blockListNames []string) (blocks map[string]block.Blocklist, err error) {
|
||||
r.logger.Debug("Reload blocklist")
|
||||
blocks = make(map[string]block.Blocklist)
|
||||
for _, blockListName := range blockListNames {
|
||||
if blockListName == "" {
|
||||
continue
|
||||
}
|
||||
r.logger.Debug(fmt.Sprintf("Reload blocklist from %s", blockListName))
|
||||
blockList, err := block.NewBlocklist(r.nft, builder, r.table.family, r.table.name, getBlocklistName(blockListName))
|
||||
if err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to create blocklist: %s", err))
|
||||
continue
|
||||
}
|
||||
if err := blockList.AddRuleToChain(afterLocalInput.AddRule, "drop"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule to chain: %s", err))
|
||||
continue
|
||||
}
|
||||
blocks[blockListName] = blockList
|
||||
}
|
||||
|
||||
return blocks, nil
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package reload
|
||||
|
||||
import (
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/firewall"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/chain"
|
||||
)
|
||||
|
||||
func (r *reload) docker(builder nft.BatchBuilder, batchForward chain.Chain, dockerChains firewall.NFTDockerChains) error {
|
||||
r.logger.Debug("Reload docker rules")
|
||||
|
||||
for _, dockerChain := range dockerChains.List() {
|
||||
if err := r.addChain(builder, dockerChain.Name(), nftChain.TypeNone); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := batchForward.AddRule("jump docker_forward_filter"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return r.dockerPrerouting(builder, dockerChains)
|
||||
}
|
||||
|
||||
func (r *reload) dockerPrerouting(builder nft.BatchBuilder, dockerChains firewall.NFTDockerChains) error {
|
||||
var rule []string
|
||||
|
||||
preroutingNat, err := r.addChainWithReturn(builder, "prerouting_nat", nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeNat,
|
||||
Hook: nftChain.HookPrerouting,
|
||||
Priority: -100,
|
||||
Policy: nftChain.PolicyAccept,
|
||||
Device: "",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rule = []string{"fib daddr type local counter jump ", dockerChains.DockerNat().Name()}
|
||||
if err := preroutingNat.AddRule(rule...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
preroutingFilter, err := r.addChainWithReturn(builder, "prerouting_filter", nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeFilter,
|
||||
Hook: nftChain.HookPrerouting,
|
||||
Priority: -300,
|
||||
Policy: nftChain.PolicyAccept,
|
||||
Device: "",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rule = []string{"jump ", dockerChains.PreroutingFilter().Name()}
|
||||
if err := preroutingFilter.AddRule(rule...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
outputNat, err := r.addChainWithReturn(builder, "output_nat", nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeNat,
|
||||
Hook: nftChain.HookOutput,
|
||||
Priority: -100,
|
||||
Policy: nftChain.PolicyAccept,
|
||||
Device: "",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rule = []string{"ip daddr != 127.0.0.0/8 fib daddr type local counter jump ", dockerChains.DockerNat().Name()}
|
||||
if err := outputNat.AddRule(rule...); err != nil {
|
||||
return err
|
||||
}
|
||||
rule = []string{"ip6 daddr != ::1 fib daddr type local counter jump ", dockerChains.DockerNat().Name()}
|
||||
if err := outputNat.AddRule(rule...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
postroutingNat, err := r.addChainWithReturn(builder, "postrouting_nat", nftChain.BaseChainOptions{
|
||||
Type: nftChain.TypeNat,
|
||||
Hook: nftChain.HookPostrouting,
|
||||
Priority: 300,
|
||||
Policy: nftChain.PolicyAccept,
|
||||
Device: "",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rule = []string{"jump ", dockerChains.PostroutingNat().Name()}
|
||||
if err := postroutingNat.AddRule(rule...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package reload
|
||||
|
||||
import (
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/firewall"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/rule"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/types"
|
||||
)
|
||||
|
||||
func (r *reload) forward(builder nft.BatchBuilder, dockerChains firewall.NFTDockerChains) error {
|
||||
r.logger.Debug("Reloading forward chain")
|
||||
|
||||
batchForward, err := chain.NewBatchForward(
|
||||
builder,
|
||||
r.table.family,
|
||||
r.table.name,
|
||||
r.config.MetadataNaming.ChainForwardName,
|
||||
r.config.Policy.DefaultAllowForward,
|
||||
r.config.Policy.ForwardPriority,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
localForward, err := chain.NewBatchChain(builder, r.table.family, r.table.name, "local-forward")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.forwardAddIPs(batchForward, localForward); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if r.config.Options.DockerSupport && dockerChains != nil {
|
||||
if err := r.docker(builder, batchForward, dockerChains); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if r.config.Policy.DefaultAllowForward == false {
|
||||
drop := r.config.Policy.ForwardDrop.String()
|
||||
if err := batchForward.AddRule(drop); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *reload) forwardAddIPs(batchForward chain.Chain, localForward chain.Chain) error {
|
||||
if err := localForward.AddRuleIn(batchForward.AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, ipConfig := range r.config.IP4.InIPs {
|
||||
if ipConfig.Action != types.ActionDrop && ipConfig.Action != types.ActionReject {
|
||||
continue
|
||||
}
|
||||
if err := rule.ForwardAddIP(localForward.AddRule, ipConfig, "ip"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !r.config.IP6.Enable {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, ipConfig := range r.config.IP6.InIPs {
|
||||
if ipConfig.Action != types.ActionDrop && ipConfig.Action != types.ActionReject {
|
||||
continue
|
||||
}
|
||||
if err := rule.ForwardAddIP(localForward.AddRule, ipConfig, "ip6"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
package reload
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/rule"
|
||||
nftTable "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/table"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/pkg"
|
||||
)
|
||||
|
||||
func (r *reload) input(builder nft.BatchBuilder, packetfilter chain.PacketFilter, blockListNames []string) (nftTable.BlockList, error) {
|
||||
r.logger.Debug("Reloading input chain")
|
||||
|
||||
batchInput, err := chain.NewBatchInput(
|
||||
builder,
|
||||
r.table.family,
|
||||
r.table.name,
|
||||
r.config.MetadataNaming.ChainInputName,
|
||||
r.config.Policy.DefaultAllowInput,
|
||||
r.config.Policy.InputPriority,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.reloadInputDnsNs(batchInput); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := batchInput.AddRule("iifname lo counter accept"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
beforeLocalInput, err := chain.NewBatchChain(builder, r.table.family, r.table.name, "before-local-input")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := beforeLocalInput.AddRuleIn(batchInput.AddRule); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
localInput, err := chain.NewBatchChain(builder, r.table.family, r.table.name, "local-input")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := r.inputAddIPs(builder, batchInput, localInput); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
afterLocalInput, err := chain.NewBatchChain(builder, r.table.family, r.table.name, "after-local-input")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := afterLocalInput.AddRuleIn(batchInput.AddRule); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := packetfilter.AddRuleIn(batchInput.AddRule); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.inputICMP(batchInput); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := batchInput.AddRule("iifname != \"lo\" ct state related,established counter accept"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.inputPorts(batchInput); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if r.config.Policy.DefaultAllowInput == false {
|
||||
drop := r.config.Policy.InputDrop.String()
|
||||
if err := batchInput.AddRule("iifname != \"lo\" " + drop); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
blocks, err := r.moduleBlockList(builder, afterLocalInput, blockListNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.blockList(builder, beforeLocalInput, blocks)
|
||||
}
|
||||
|
||||
func (r *reload) reloadInputDnsNs(batchInput chain.Chain) error {
|
||||
if r.config.Options.DnsStrictNs {
|
||||
return nil
|
||||
}
|
||||
|
||||
addresses, err := pkg.Resolv.Addresses()
|
||||
if err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to get nameservers: %s", err))
|
||||
return nil
|
||||
}
|
||||
for _, addr := range addresses {
|
||||
ip := net.ParseIP(addr)
|
||||
if ip == nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to parse nameserver address: %s", addr))
|
||||
continue
|
||||
}
|
||||
if ip.To4() != nil {
|
||||
if err := batchInput.AddRule("ip saddr " + addr + " iifname != \"lo\" tcp dport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := batchInput.AddRule("ip saddr " + addr + " iifname != \"lo\" udp dport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := batchInput.AddRule("ip saddr " + addr + " iifname != \"lo\" tcp sport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := batchInput.AddRule("ip saddr " + addr + " iifname != \"lo\" udp sport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if ip.To16() != nil {
|
||||
if !r.config.IP6.Enable {
|
||||
r.logger.Warn(fmt.Sprintf("IPv6 is disabled, skipping nameserver address: %s", addr))
|
||||
continue
|
||||
}
|
||||
if err := batchInput.AddRule("ip6 saddr " + addr + " iifname != \"lo\" tcp dport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := batchInput.AddRule("ip6 saddr " + addr + " iifname != \"lo\" udp dport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := batchInput.AddRule("ip6 saddr " + addr + " iifname != \"lo\" tcp sport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := batchInput.AddRule("ip6 saddr " + addr + " iifname != \"lo\" udp sport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
r.logger.Error(fmt.Sprintf("Failed to parse nameserver address: %s", addr))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *reload) inputAddIPs(builder nft.BatchBuilder, batchInput chain.Chain, localInput chain.Chain) error {
|
||||
if err := localInput.AddRuleIn(batchInput.AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.inputPortKnocking(builder, localInput); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, ipConfig := range r.config.IP4.InIPs {
|
||||
if err := rule.InputAddIP(localInput.AddRule, ipConfig, "ip"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !r.config.IP6.Enable {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, ipConfig := range r.config.IP6.InIPs {
|
||||
if err := rule.InputAddIP(localInput.AddRule, ipConfig, "ip6"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *reload) inputPortKnocking(builder nft.BatchBuilder, localInput chain.Chain) error {
|
||||
if len(r.config.PortKnocking) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
portKnocking, err := chain.NewBatchPortKnocking(builder, r.table.family, r.table.name, "port_knocking")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, portKnockingConfig := range r.config.PortKnocking {
|
||||
var knockName, prevKnockName string
|
||||
for index, knock := range portKnockingConfig.Knocks {
|
||||
prevKnockName = knockName
|
||||
knockName = fmt.Sprintf("knock_%s_%d", portKnockingConfig.Name, index)
|
||||
if index == 0 {
|
||||
if err := portKnocking.AddFirstStageRule(knockName, portKnockingConfig.IPVersion, knock.Port, knock.Timeout, knock.Action); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if err := portKnocking.AddNextStageRule(prevKnockName, knockName, portKnockingConfig.IPVersion, knock.Port, knock.Timeout, knock.Action); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
expr := []string{
|
||||
portKnockingConfig.IPVersion.ToNft(), "saddr", "@" + knockName,
|
||||
portKnockingConfig.Port.ProtocolString(), "dport", portKnockingConfig.Port.NumberString(), "accept",
|
||||
}
|
||||
if err := localInput.AddRule(expr...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := portKnocking.AddRuleIn(localInput.AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *reload) inputICMP(batchInput chain.Chain) error {
|
||||
drop := r.config.Policy.InputDrop.String()
|
||||
if r.config.IP4.IcmpIn == false {
|
||||
if err := batchInput.AddRule("iifname != \"lo\" ip protocol icmp icmp type echo-request counter " + drop); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.inputICMPAfter(batchInput)
|
||||
}
|
||||
|
||||
if r.config.IP4.IcmpInRate == "0" {
|
||||
return r.inputICMPAfter(batchInput)
|
||||
}
|
||||
|
||||
if err := batchInput.AddRule("iifname != \"lo\" ip protocol icmp icmp type echo-request limit rate " + r.config.IP4.IcmpInRate + " counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchInput.AddRule("iifname != \"lo\" ip protocol icmp icmp type echo-request counter " + drop); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return r.inputICMPAfter(batchInput)
|
||||
}
|
||||
|
||||
func (r *reload) inputICMPAfter(batchInput chain.Chain) error {
|
||||
if r.config.IP4.IcmpTimestampDrop == true {
|
||||
drop := r.config.Policy.InputDrop.String()
|
||||
if err := batchInput.AddRule("iifname != \"lo\" ip protocol icmp icmp type timestamp-request " + drop); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := batchInput.AddRule("iifname != \"lo\" ip protocol icmp counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if r.config.IP6.Enable {
|
||||
if r.config.IP6.IcmpStrict {
|
||||
return r.inputICMP6Strict(batchInput)
|
||||
} else if err := batchInput.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *reload) inputICMP6Strict(batchInput chain.Chain) error {
|
||||
if err := batchInput.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type destination-unreachable counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchInput.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type packet-too-big counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchInput.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type time-exceeded counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchInput.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type parameter-problem counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchInput.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type echo-request counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchInput.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type echo-reply counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchInput.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type nd-router-advert ip6 hoplimit 255 counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchInput.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type nd-neighbor-solicit ip6 hoplimit 255 counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchInput.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type nd-neighbor-advert ip6 hoplimit 255 counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchInput.AddRule("iifname != \"lo\" meta l4proto ipv6-icmp icmpv6 type nd-redirect ip6 hoplimit 255 counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *reload) inputPorts(batchInput chain.Chain) error {
|
||||
for _, port := range r.config.InPorts {
|
||||
protocol := port.Port.ProtocolString()
|
||||
number := port.Port.NumberString()
|
||||
|
||||
baseRule := "iifname != \"lo\" meta l4proto " + protocol + " ct state new " + protocol + " dport " + number
|
||||
|
||||
if port.LimitRate != "" {
|
||||
addRule := baseRule + " limit rate " + port.LimitRate + " counter " + port.Action.String()
|
||||
if err := batchInput.AddRule(addRule); err != nil {
|
||||
return err
|
||||
}
|
||||
addRuleDrop := baseRule + " counter " + r.config.Policy.InputDrop.String()
|
||||
if err := batchInput.AddRule(addRuleDrop); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
addRule := baseRule + " counter " + port.Action.String()
|
||||
if err := batchInput.AddRule(addRule); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
package reload
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/rule"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/pkg"
|
||||
)
|
||||
|
||||
func (r *reload) output(builder nft.BatchBuilder, packetfilter chain.PacketFilter) error {
|
||||
r.logger.Debug("Reloading output chain")
|
||||
|
||||
batchOutput, err := chain.NewBatchOutput(
|
||||
builder,
|
||||
r.table.family,
|
||||
r.table.name,
|
||||
r.config.MetadataNaming.ChainOutputName,
|
||||
r.config.Policy.DefaultAllowOutput,
|
||||
r.config.Policy.OutputPriority,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.outputDnsNs(batchOutput); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.outputDns(batchOutput); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchOutput.AddRule("oifname lo counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
localOutput, err := chain.NewBatchChain(builder, r.table.family, r.table.name, "local-output")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.outputAddIPs(batchOutput, localOutput); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := packetfilter.AddRuleOut(batchOutput.AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.outputICMP(batchOutput); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := batchOutput.AddRule("oifname != \"lo\" ct state related,established counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.outputPorts(batchOutput); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if r.config.Policy.DefaultAllowOutput == false {
|
||||
drop := r.config.Policy.OutputDrop.String()
|
||||
if err := batchOutput.AddRule("oifname != \"lo\" " + drop); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *reload) outputDnsNs(batchOutput chain.Chain) error {
|
||||
if r.config.Options.DnsStrictNs {
|
||||
return nil
|
||||
}
|
||||
|
||||
addresses, err := pkg.Resolv.Addresses()
|
||||
if err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to get nameservers: %s", err))
|
||||
return nil
|
||||
}
|
||||
for _, addr := range addresses {
|
||||
ip := net.ParseIP(addr)
|
||||
if ip == nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to parse nameserver address: %s", addr))
|
||||
continue
|
||||
}
|
||||
if ip.To4() != nil {
|
||||
if err := batchOutput.AddRule("ip daddr " + addr + " oifname != \"lo\" tcp dport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := batchOutput.AddRule("ip daddr " + addr + " oifname != \"lo\" udp dport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := batchOutput.AddRule("ip daddr " + addr + " oifname != \"lo\" tcp sport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := batchOutput.AddRule("ip daddr " + addr + " oifname != \"lo\" udp sport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if ip.To16() != nil {
|
||||
if err := batchOutput.AddRule("ip6 daddr " + addr + " oifname != \"lo\" tcp dport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := batchOutput.AddRule("ip6 daddr " + addr + " oifname != \"lo\" udp dport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := batchOutput.AddRule("ip6 daddr " + addr + " oifname != \"lo\" tcp sport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
if err := batchOutput.AddRule("ip6 daddr " + addr + " oifname != \"lo\" udp sport 53 counter accept"); err != nil {
|
||||
r.logger.Error(fmt.Sprintf("Failed to add rule: %s", err))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
r.logger.Error(fmt.Sprintf("Failed to parse nameserver address: %s", addr))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *reload) outputDns(batchOutput chain.Chain) error {
|
||||
if r.config.Options.DnsStrict {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := batchOutput.AddRule("oifname != \"lo\" tcp dport 53 counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchOutput.AddRule("oifname != \"lo\" udp dport 53 counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchOutput.AddRule("oifname != \"lo\" tcp sport 53 counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchOutput.AddRule("oifname != \"lo\" udp sport 53 counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *reload) outputAddIPs(batchOutput chain.Chain, localOutput chain.Chain) error {
|
||||
if err := localOutput.AddRuleOut(batchOutput.AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, ipConfig := range r.config.IP4.OutIPs {
|
||||
if err := rule.OutputAddIP(localOutput.AddRule, ipConfig, "ip"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !r.config.IP6.Enable {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, ipConfig := range r.config.IP6.OutIPs {
|
||||
if err := rule.OutputAddIP(localOutput.AddRule, ipConfig, "ip6"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *reload) outputICMP(batchOutput chain.Chain) error {
|
||||
drop := r.config.Policy.OutputDrop.String()
|
||||
if r.config.IP4.IcmpOut == false {
|
||||
if err := batchOutput.AddRule("oifname != \"lo\" ip protocol icmp icmp type echo-request counter " + drop); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.outputICMPAfter(batchOutput)
|
||||
}
|
||||
|
||||
if r.config.IP4.IcmpOutRate == "0" {
|
||||
return r.outputICMPAfter(batchOutput)
|
||||
}
|
||||
|
||||
if err := batchOutput.AddRule("oifname != \"lo\" ip protocol icmp icmp type echo-request limit rate " + r.config.IP4.IcmpInRate + " counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batchOutput.AddRule("oifname != \"lo\" ip protocol icmp icmp type echo-request counter " + drop); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return r.outputICMPAfter(batchOutput)
|
||||
}
|
||||
|
||||
func (r *reload) outputICMPAfter(batchOutput chain.Chain) error {
|
||||
if r.config.IP4.IcmpTimestampDrop == true {
|
||||
drop := r.config.Policy.OutputDrop.String()
|
||||
if err := batchOutput.AddRule("oifname != \"lo\" ip protocol icmp icmp type timestamp-request " + drop); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := batchOutput.AddRule("oifname != \"lo\" ip protocol icmp counter accept"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *reload) outputPorts(batchOutput chain.Chain) error {
|
||||
for _, port := range r.config.OutPorts {
|
||||
protocol := port.Port.ProtocolString()
|
||||
number := port.Port.NumberString()
|
||||
baseRule := "oifname != \"lo\" meta l4proto " + protocol + " ct state new " + protocol + " dport " + number
|
||||
|
||||
if port.LimitRate != "" {
|
||||
addRule := baseRule + " limit rate " + port.LimitRate + " counter " + port.Action.String()
|
||||
if err := batchOutput.AddRule(addRule); err != nil {
|
||||
return err
|
||||
}
|
||||
addRuleDrop := baseRule + " counter " + r.config.Policy.InputDrop.String()
|
||||
if err := batchOutput.AddRule(addRuleDrop); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
addRule := baseRule + " counter " + port.Action.String()
|
||||
if err := batchOutput.AddRule(addRule); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package reload
|
||||
|
||||
import (
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/chain"
|
||||
)
|
||||
|
||||
func (r *reload) packetFilter(builder nft.BatchBuilder) (chain.PacketFilter, error) {
|
||||
if !r.config.Options.PacketFilter {
|
||||
return chain.NewPacketFilterFalse(), nil
|
||||
}
|
||||
|
||||
chainInvalidName := "INVALID"
|
||||
|
||||
chainName := "INVDROP"
|
||||
|
||||
if err := r.addChain(builder, chainName, nftChain.TypeNone); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := r.addRule(builder, chainName, "counter drop"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.addChain(builder, chainInvalidName, nftChain.TypeNone); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.addRule(builder, chainInvalidName, "ct state invalid counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.addRule(builder, chainInvalidName, "tcp flags ! fin,syn,rst,psh,ack,urg counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.addRule(builder, chainInvalidName, "tcp flags & (fin | syn | rst | psh | ack | urg) == fin | syn | rst | psh | ack | urg counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.addRule(builder, chainInvalidName, "tcp flags & (fin | syn) == fin | syn counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.addRule(builder, chainInvalidName, "tcp flags & (syn | rst) == syn | rst counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.addRule(builder, chainInvalidName, "tcp flags & (fin | rst) == fin | rst counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.addRule(builder, chainInvalidName, "tcp flags & (fin | ack) == fin counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.addRule(builder, chainInvalidName, "tcp flags & (psh | ack) == psh counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.addRule(builder, chainInvalidName, "tcp flags & (ack | urg) == urg counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.addRule(builder, chainInvalidName, "tcp flags & (fin | syn | rst | ack) != syn ct state new counter jump INVDROP"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return chain.NewPacketFilter(chainInvalidName), nil
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
package reload
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
nftChain "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain"
|
||||
"git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/docker_monitor/firewall"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/config"
|
||||
nftFirewall "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/block"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/chain"
|
||||
dataTable "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/nft/table"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
|
||||
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract"
|
||||
)
|
||||
|
||||
const blockedIP = "block_ip"
|
||||
const blockedIPWithPort = "block_ip_with_port"
|
||||
|
||||
type Reload interface {
|
||||
RunWithCache(file string, isValidCacheFile bool, blockListNames []string) (dataTable.Table, error)
|
||||
Run(blockListNames []string) (dataTable.Table, error)
|
||||
}
|
||||
|
||||
type table struct {
|
||||
name string
|
||||
family family.Type
|
||||
}
|
||||
|
||||
type reload struct {
|
||||
nft nftFirewall.NFT
|
||||
logger log.Logger
|
||||
config *config.Config
|
||||
table *table
|
||||
}
|
||||
|
||||
func New(nft nftFirewall.NFT, logger log.Logger, config *config.Config) Reload {
|
||||
return &reload{
|
||||
nft: nft,
|
||||
logger: logger,
|
||||
config: config,
|
||||
table: &table{
|
||||
name: config.MetadataNaming.TableName,
|
||||
family: family.INET,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *reload) RunWithCache(file string, isValidCacheFile bool, blockListNames []string) (dataTable.Table, error) {
|
||||
if file == "" {
|
||||
r.logger.Warn("file is empty, using default reload")
|
||||
return r.Run(blockListNames)
|
||||
}
|
||||
if isValidCacheFile {
|
||||
r.logger.Debug("use cache file")
|
||||
table, err := r.loadCache(file, blockListNames)
|
||||
if err == nil {
|
||||
return table, nil
|
||||
}
|
||||
r.logger.Warn(fmt.Sprintf("failed to load cache file: %s", err))
|
||||
}
|
||||
|
||||
batchBuilder, err := r.nft.NewBuildBatch()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err := batchBuilder.Close(); err != nil {
|
||||
r.logger.Warn(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
table, err := r.reload(batchBuilder, blockListNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.nft.RunBatchAndMoveFile(batchBuilder, file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return table, nil
|
||||
}
|
||||
|
||||
func (r *reload) Run(blockListNames []string) (dataTable.Table, error) {
|
||||
batchBuilder, err := r.nft.NewBuildBatch()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err := batchBuilder.Close(); err != nil {
|
||||
r.logger.Warn(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
table, err := r.reload(batchBuilder, blockListNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.nft.RunBatch(batchBuilder); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return table, nil
|
||||
}
|
||||
|
||||
func (r *reload) reload(batchBuilder nft.BatchBuilder, blockListNames []string) (dataTable.Table, error) {
|
||||
var dockerChains firewall.NFTDockerChains
|
||||
if r.config.Options.DockerSupport {
|
||||
dockerChains = firewall.NewNFTChains(r.nft, r.table.family, r.table.name)
|
||||
}
|
||||
|
||||
if err := r.clear(batchBuilder); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
packetFilter, err := r.packetFilter(batchBuilder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blockList, err := r.input(batchBuilder, packetFilter, blockListNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.output(batchBuilder, packetFilter); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := r.forward(batchBuilder, dockerChains); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dataTable.New(
|
||||
r.nft, r.table.family, r.table.name,
|
||||
blockList, dockerChains,
|
||||
), nil
|
||||
}
|
||||
|
||||
func (r *reload) clear(builder nft.BatchBuilder) error {
|
||||
switch r.config.Options.ClearMode {
|
||||
case config.ClearModeGlobal:
|
||||
if err := builder.Clear(); err != nil {
|
||||
return fmt.Errorf("failed to clear global rules: %w", err)
|
||||
}
|
||||
if err := builder.Table().Add(r.table.family, r.table.name); err != nil {
|
||||
return fmt.Errorf("failed to add table: %w", err)
|
||||
}
|
||||
break
|
||||
case config.ClearModeOwn:
|
||||
if err := builder.Table().Add(r.table.family, r.table.name); err != nil {
|
||||
return fmt.Errorf("failed to add table: %w", err)
|
||||
}
|
||||
// clear does not clean completely
|
||||
if err := builder.Table().Delete(r.table.family, r.table.name); err != nil {
|
||||
return fmt.Errorf("failed to clear table: %w", err)
|
||||
}
|
||||
if err := builder.Table().Add(r.table.family, r.table.name); err != nil {
|
||||
return fmt.Errorf("failed to add table: %w", err)
|
||||
}
|
||||
break
|
||||
default:
|
||||
return fmt.Errorf("unknown clear mode: %d", r.config.Options.ClearMode)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *reload) loadCache(file string, blockListNames []string) (dataTable.Table, error) {
|
||||
args := []string{"-f", file}
|
||||
if err := r.nft.NFT().Command().Run(args...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var dockerChains firewall.NFTDockerChains
|
||||
if r.config.Options.DockerSupport {
|
||||
dockerChains = firewall.NewNFTChains(r.nft, r.table.family, r.table.name)
|
||||
}
|
||||
|
||||
blocks := make(map[string]block.Blocklist)
|
||||
for _, blockListName := range blockListNames {
|
||||
if blockListName == "" {
|
||||
continue
|
||||
}
|
||||
blockList := block.NewBlocklistWithoutCommand(r.nft, r.table.family, r.table.name, getBlocklistName(blockListName))
|
||||
blocks[blockListName] = blockList
|
||||
}
|
||||
|
||||
listBlockedIP := block.NewListIPWithoutCommand(r.nft, r.table.family, r.table.name, blockedIP)
|
||||
listBlockedIPWithPort := block.NewListIPWithPortWithoutCommand(r.nft, r.table.family, r.table.name, blockedIPWithPort)
|
||||
|
||||
tableBlocklist := dataTable.NewBlockList(listBlockedIP, listBlockedIPWithPort, blocks)
|
||||
return dataTable.New(
|
||||
r.nft, r.table.family, r.table.name,
|
||||
tableBlocklist, dockerChains,
|
||||
), nil
|
||||
}
|
||||
|
||||
func (r *reload) addChain(builder nft.BatchBuilder, chainName string, baseChain nftChain.ChainOptions) error {
|
||||
return builder.Chain().Add(r.table.family, r.table.name, chainName, baseChain)
|
||||
}
|
||||
|
||||
func (r *reload) addRule(builder nft.BatchBuilder, chainName string, rule string) error {
|
||||
return builder.Rule().Add(r.table.family, r.table.name, chainName, rule)
|
||||
}
|
||||
|
||||
func (r *reload) addChainWithReturn(builder nft.BatchBuilder, chainName string, baseChain nftChain.ChainOptions) (chain.Chain, error) {
|
||||
return chain.NewBatchChainWithOptions(builder, r.table.family, r.table.name, chainName, baseChain)
|
||||
}
|
||||
|
||||
func getBlocklistName(blockListName string) string {
|
||||
return "blocklist_" + blockListName
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package firewall
|
||||
|
||||
func (f *firewall) reloadBlockList() error {
|
||||
listBlockedIP, err := f.chains.NewBlockListIP("blocked_ip")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := listBlockedIP.AddRuleToChain(f.chains.BeforeLocalInput().AddRule, "drop"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
listBlockedIPWithPort, err := f.chains.NewBlockListIPWithPort("blocked_ip_port")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := listBlockedIPWithPort.AddRuleToChain(f.chains.BeforeLocalInput().AddRule, "drop"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := f.blockingService.NftReload(listBlockedIP, listBlockedIPWithPort); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
package firewall
|
||||
|
||||
import "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/types"
|
||||
|
||||
func (f *firewall) reloadForward() error {
|
||||
f.logger.Debug("Reloading forward chain")
|
||||
err := f.chains.NewForward(f.config.MetadataNaming.ChainForwardName, f.config.Policy.DefaultAllowForward, f.config.Policy.ForwardPriority)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
chain := f.chains.Forward()
|
||||
|
||||
if err := f.reloadForwardAddIPs(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if f.config.Options.DockerSupport {
|
||||
if err := f.docker.NftChains().ForwardFilterJump(chain.AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if f.config.Policy.DefaultAllowForward == false {
|
||||
drop := f.config.Policy.ForwardDrop.String()
|
||||
if err := chain.AddRule(drop); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *firewall) reloadForwardAddIPs() error {
|
||||
if err := f.chains.NewLocalForward(); err != nil {
|
||||
return err
|
||||
}
|
||||
chain := f.chains.LocalForward()
|
||||
if err := chain.AddRuleIn(f.chains.Forward().AddRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, ipConfig := range f.config.IP4.InIPs {
|
||||
if ipConfig.Action != types.ActionDrop && ipConfig.Action != types.ActionReject {
|
||||
continue
|
||||
}
|
||||
if err := forwardAddIP(chain.AddRule, ipConfig, "ip"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !f.config.IP6.Enable {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, ipConfig := range f.config.IP6.InIPs {
|
||||
if ipConfig.Action != types.ActionDrop && ipConfig.Action != types.ActionReject {
|
||||
continue
|
||||
}
|
||||
if err := forwardAddIP(chain.AddRule, ipConfig, "ip6"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func forwardAddIP(addRuleFunc func(expr ...string) error, config ConfigIP, ipMatch string) error {
|
||||
rule := ipMatch + " saddr " + config.IP + " iifname != \"lo\""
|
||||
|
||||
// There, during routing, the port changes and then the IP blocking rule will not work.
|
||||
//if !config.OnlyIP {
|
||||
// rule += " " + config.Protocol.String() + " dport " + strconv.Itoa(int(config.Port))
|
||||
//}
|
||||
|
||||
rule += " counter " + config.Action.String()
|
||||
if err := addRuleFunc(rule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package info
|
||||
|
||||
import (
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db/entity"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db/repository"
|
||||
)
|
||||
|
||||
func NewMetadataFirewallFileNft(repo repository.MetadataRepository) Metadata {
|
||||
return NewMetadata(repo, entity.MetadataKeyFirewallFileNft)
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package info
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type Info interface {
|
||||
// Version returns the version of the daemon.
|
||||
Version() string
|
||||
|
||||
// IsVersionChanged returns true if the version of the daemon has changed.
|
||||
IsVersionChanged() bool
|
||||
|
||||
// IsSettingsChanged returns true if the settings of the daemon has changed.
|
||||
IsSettingsChanged() bool
|
||||
|
||||
// BuiltWith returns the build information of the daemon.
|
||||
BuiltWith() string
|
||||
// StartTime returns the start time of the daemon.
|
||||
StartTime() time.Time
|
||||
// Uptime returns the uptime of the daemon.
|
||||
Uptime() time.Duration
|
||||
|
||||
// Metadata returns the metadata of the daemon.
|
||||
Metadata() MetadataContainer
|
||||
}
|
||||
|
||||
type info struct {
|
||||
version string
|
||||
isVersionChanged bool
|
||||
|
||||
isSettingsChanged bool
|
||||
|
||||
builtWith string
|
||||
startTime time.Time
|
||||
|
||||
metadata MetadataContainer
|
||||
}
|
||||
|
||||
func New(
|
||||
version string,
|
||||
isVersionChanged bool,
|
||||
|
||||
builtWith string,
|
||||
startTime time.Time,
|
||||
|
||||
isSettingsChanged bool,
|
||||
|
||||
metadata MetadataContainer,
|
||||
) Info {
|
||||
return &info{
|
||||
version: version,
|
||||
isVersionChanged: isVersionChanged,
|
||||
|
||||
isSettingsChanged: isSettingsChanged,
|
||||
|
||||
builtWith: builtWith,
|
||||
startTime: startTime,
|
||||
metadata: metadata,
|
||||
}
|
||||
}
|
||||
|
||||
func (i *info) Version() string {
|
||||
return i.version
|
||||
}
|
||||
|
||||
func (i *info) IsVersionChanged() bool {
|
||||
return i.isVersionChanged
|
||||
}
|
||||
|
||||
func (i *info) IsSettingsChanged() bool {
|
||||
return i.isSettingsChanged
|
||||
}
|
||||
|
||||
func (i *info) BuiltWith() string {
|
||||
return i.builtWith
|
||||
}
|
||||
|
||||
func (i *info) StartTime() time.Time {
|
||||
return i.startTime
|
||||
}
|
||||
|
||||
func (i *info) Uptime() time.Duration {
|
||||
return time.Since(i.StartTime())
|
||||
}
|
||||
|
||||
func (i *info) Metadata() MetadataContainer {
|
||||
return i.metadata
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package info
|
||||
|
||||
import (
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db/entity"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db/repository"
|
||||
)
|
||||
|
||||
type MetadataContainer interface {
|
||||
FirewallFileNft() Metadata
|
||||
}
|
||||
|
||||
type Metadata interface {
|
||||
Get() (string, error)
|
||||
Update(data string) error
|
||||
}
|
||||
|
||||
func NewMetadataContainer(
|
||||
firewallFileNft Metadata,
|
||||
) MetadataContainer {
|
||||
return &metadataContainer{
|
||||
firewallFileNft: firewallFileNft,
|
||||
}
|
||||
}
|
||||
|
||||
func NewMetadata(repo repository.MetadataRepository, key string) Metadata {
|
||||
return &metadata{
|
||||
key: key,
|
||||
repo: repo,
|
||||
}
|
||||
}
|
||||
|
||||
type metadataContainer struct {
|
||||
firewallFileNft Metadata
|
||||
}
|
||||
|
||||
func (m *metadataContainer) FirewallFileNft() Metadata {
|
||||
return m.firewallFileNft
|
||||
}
|
||||
|
||||
type metadata struct {
|
||||
key string
|
||||
repo repository.MetadataRepository
|
||||
}
|
||||
|
||||
func (m *metadata) Get() (string, error) {
|
||||
meta, err := m.repo.Get(m.key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return meta.Value, nil
|
||||
}
|
||||
|
||||
func (m *metadata) Update(value string) error {
|
||||
meta := &entity.Metadata{Value: value}
|
||||
return m.repo.Update(m.key, meta)
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package info
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db/entity"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db/repository"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
)
|
||||
|
||||
func IsSettingsChanged(repo repository.MetadataRepository, listPathFiles map[string]string, logger log.Logger) bool {
|
||||
isChanged := false
|
||||
|
||||
for k, f := range listPathFiles {
|
||||
hash, err := fileHash(f)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("Failed to get hash of %s: %s", f, err))
|
||||
continue
|
||||
}
|
||||
|
||||
settingMetadata := NewMetadata(repo, entity.KeySetting(k))
|
||||
metadataValue, err := settingMetadata.Get()
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("Failed to get metadata value: %s", err))
|
||||
}
|
||||
|
||||
if metadataValue != hash {
|
||||
isChanged = true
|
||||
if err := settingMetadata.Update(hash); err != nil {
|
||||
logger.Error(fmt.Sprintf("Failed to update metadata: %s", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isChanged
|
||||
}
|
||||
|
||||
func fileHash(path string) (string, error) {
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
size := info.Size()
|
||||
modTime := info.ModTime()
|
||||
|
||||
return fmt.Sprintf("%d-%d", size, modTime.Unix()), nil
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package info
|
||||
|
||||
import (
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db/entity"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db/repository"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/log"
|
||||
)
|
||||
|
||||
func IsVersionChanged(repo repository.MetadataRepository, version string, logger log.Logger) bool {
|
||||
metadata := NewMetadata(repo, entity.MetadataKeyVersion)
|
||||
if v, err := metadata.Get(); err != nil {
|
||||
logger.Error(err.Error())
|
||||
return false
|
||||
} else if v != version {
|
||||
if err := metadata.Update(version); err != nil {
|
||||
logger.Error(err.Error())
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/analyzer/config"
|
||||
analyzerConfig "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/analyzer/config"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/db"
|
||||
"git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall"
|
||||
firewallConfig "git.kor-elf.net/kor-elf-shield/kor-elf-shield/internal/daemon/firewall/config"
|
||||
)
|
||||
|
||||
type DaemonOptions struct {
|
||||
@@ -11,7 +11,7 @@ type DaemonOptions struct {
|
||||
PathSocketFile string
|
||||
DataDir string
|
||||
PathNftables string
|
||||
ConfigFirewall firewall.Config
|
||||
ConfigAnalyzer config.Config
|
||||
ConfigFirewall firewallConfig.Config
|
||||
ConfigAnalyzer analyzerConfig.Config
|
||||
Repositories db.Repositories
|
||||
}
|
||||
|
||||
@@ -2,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)
|
||||
|
||||
@@ -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}})",
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
"user": "Пайдаланушы",
|
||||
"access to user has been gained": "Пайдаланушыға кіру мүмкіндігі алынды",
|
||||
"unknown": "белгісіз",
|
||||
"blockSec": "{{.BlockSec}} секундқа блокталды",
|
||||
"blockSec": "{{.BlockSec}} блокталды",
|
||||
"ports": "Порттар: {{.Ports}}",
|
||||
|
||||
"alert.subject": "Ескерту анықталды ({{.Name}}) (топ:{{.GroupName}})",
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
"user": "Пользователь",
|
||||
"access to user has been gained": "Получен доступ к пользователю",
|
||||
"unknown": "неизвестный",
|
||||
"blockSec": "Блокировка на {{.BlockSec}} секунд",
|
||||
"blockSec": "Блокировка на {{.BlockSec}}",
|
||||
"ports": "Порты: {{.Ports}}",
|
||||
|
||||
"alert.subject": "Обнаружено оповещение ({{.Name}}) (группа:{{.GroupName}})",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'")
|
||||
}
|
||||
|
||||
@@ -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,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,
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -31,7 +31,7 @@ func InitSetting(path string) error {
|
||||
}
|
||||
|
||||
// Default config
|
||||
Config = settingDefault()
|
||||
Config = settingDefault(path)
|
||||
|
||||
v := viper.New()
|
||||
v.SetConfigType("toml")
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user