Merge pull request 'v0.2.0' (#2) from develop into main

Reviewed-on: #2
This commit was merged in pull request #2.
This commit is contained in:
2025-11-29 16:12:03 +05:00
14 changed files with 195 additions and 22 deletions

View File

@@ -1,3 +1,17 @@
## 0.2.0 (29.11.2025)
***
#### Русский
* Добавлен параметр clear_mode в firewall.toml. Он позволяет переключать режим очистки всех правил в nftables или только таблицу относящие к программе.
* Добавлен параметр input_priority в firewall.toml. Можно указать приоритет от -50 по 50 к chain input.
* Добавлен параметр output_priority в firewall.toml. Можно указать приоритет от -50 по 50 к chain output.
* Добавлен параметр forward_priority в firewall.toml. Можно указать приоритет от -50 по 50 к chain forward.
***
#### English
* Added the clear_mode parameter to firewall.toml. It allows you to toggle clearing of all rules in nftables or only the program-specific table.
* Added the input_priority parameter to firewall.toml. You can specify a priority from -50 to 50 for chain input.
* Added the output_priority parameter to firewall.toml. You can specify a priority from -50 to 50 for chain output.
* Added the forward_priority parameter to firewall.toml. You can specify a priority from -50 to 50 for chain forward.
***
## 0.1.0 (8.11.2025)
***
#### Русский

View File

@@ -299,6 +299,26 @@ icmp_strict = false
# SECTION:General Settings
###############################################################################
[options]
###
# Переключения режима очистки фаервола nftables. Если указать "own", то может получиться конфликт в правилах.
# Может спровоцировать проблему в безопасности. Указывайте "own" если вы уверены в своих действиях.
# Допустимые значения:
# global = очищает полностью все правила
# own = очищает только правила от таблицы, которые указаны в параметре table_name
#
# По умолчанию: global
# ***
# Switching the nftables firewall cleaning mode. If you specify "own", a conflict in the rules may occur.
# This may cause a security issue. Use "own" if you are confident in your actions.
# Valid values:
# global = clears all rules completely
# own = clears only the rules from the table that are specified in the table_name parameter
#
# Default: global
###
clear_mode = "global"
###
# Будет ли демон сохранять правила в системный файл nftables.
# Не забудьте проверить, что путь к nftables соответствует вашей ОС.
@@ -409,6 +429,21 @@ default_allow_forward = false
###
input_drop = "drop"
###
# Приоритет chain для input.
# От: -50
# По: 50
#
# По умолчанию: -10
# ***
# Chain priority for input.
# From: -50
# To: 50
#
# Default: -10
###
input_priority = -10
###
# Как заблокировать исходящий трафик. Блокировать молча или с обратной связью.
# Допустимые значения:
@@ -426,6 +461,21 @@ input_drop = "drop"
###
output_drop = "reject"
###
# Приоритет chain для output.
# От: -50
# По: 50
#
# По умолчанию: -10
# ***
# Chain priority for output.
# From: -50
# To: 50
#
# Default: -10
###
output_priority = -10
###
# Как заблокировать трафик forward. Блокировать молча или с обратной связью.
# Допустимые значения:
@@ -443,6 +493,21 @@ output_drop = "reject"
###
forward_drop = "drop"
###
# Приоритет chain для forward.
# От: -50
# По: 50
#
# По умолчанию: -10
# ***
# Chain priority for forward.
# From: -50
# To: 50
#
# Default: -10
###
forward_priority = -10
###############################################################################
# РАЗДЕЛ:Именование метаданных
# ***

View File

@@ -1,6 +1,8 @@
package chain
import (
"strings"
nft "git.kor-elf.net/kor-elf-shield/go-nftables-client"
nftFamily "git.kor-elf.net/kor-elf-shield/go-nftables-client/family"
)
@@ -9,13 +11,13 @@ type Chains interface {
NewPacketFilter(enable bool) error
PacketFilter() PacketFilter
NewInput(chain string, defaultAllow bool) error
NewInput(chain string, defaultAllow bool, priority int) error
Input() Input
NewOutput(chain string, defaultAllow bool) error
NewOutput(chain string, defaultAllow bool, priority int) error
Output() Output
NewForward(chain string, defaultAllow bool) error
NewForward(chain string, defaultAllow bool, priority int) error
Forward() Forward
NewLocalInput() error
@@ -23,6 +25,8 @@ type Chains interface {
NewLocalOutput() error
LocalOutput() LocalOutput
ClearRules() error
}
type chains struct {
@@ -40,11 +44,12 @@ type chains struct {
}
func NewChains(nft nft.NFT, table string) (Chains, error) {
if err := nft.Clear(); err != nil {
family := nftFamily.INET
if err := clearRules(nft, family, table); err != nil {
return nil, err
}
family := nftFamily.INET
if err := nft.Table().Add(family, table); err != nil {
return nil, err
}
@@ -70,8 +75,8 @@ func (c *chains) PacketFilter() PacketFilter {
return c.packetFilter
}
func (c *chains) NewInput(chain string, defaultAllow bool) error {
input, err := newInput(c.nft, c.family, c.table, chain, defaultAllow)
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
}
@@ -84,8 +89,8 @@ func (c *chains) Input() Input {
return c.input
}
func (c *chains) NewOutput(chain string, defaultAllow bool) error {
output, err := newOutput(c.nft, c.family, c.table, chain, defaultAllow)
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
}
@@ -98,8 +103,8 @@ func (c *chains) Output() Output {
return c.output
}
func (c *chains) NewForward(chain string, defaultAllow bool) error {
forward, err := newForward(c.nft, c.family, c.table, chain, defaultAllow)
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
}
@@ -137,3 +142,17 @@ func (c *chains) NewLocalOutput() error {
func (c *chains) LocalOutput() LocalOutput {
return c.localOutput
}
func (c *chains) ClearRules() error {
return clearRules(c.nft, c.family, c.table)
}
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
}

View File

@@ -17,7 +17,7 @@ type forward struct {
chain string
}
func newForward(nft nft.NFT, family family.Type, table string, chain string, defaultAllow bool) (Forward, error) {
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
@@ -26,7 +26,7 @@ func newForward(nft nft.NFT, family family.Type, table string, chain string, def
baseChain := nftChain.BaseChainOptions{
Type: nftChain.TypeFilter,
Hook: nftChain.HookForward,
Priority: 0,
Priority: int32(priority),
Policy: policy,
Device: "",
}

View File

@@ -17,7 +17,7 @@ type input struct {
chain string
}
func newInput(nft nft.NFT, family family.Type, table string, chain string, defaultAllow bool) (Input, error) {
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
@@ -26,7 +26,7 @@ func newInput(nft nft.NFT, family family.Type, table string, chain string, defau
baseChain := nftChain.BaseChainOptions{
Type: nftChain.TypeFilter,
Hook: nftChain.HookInput,
Priority: 0,
Priority: int32(priority),
Policy: policy,
Device: "",
}

View File

@@ -17,7 +17,7 @@ type output struct {
chain string
}
func newOutput(nft nft.NFT, family family.Type, table string, chain string, defaultAllow bool) (Output, error) {
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
@@ -26,7 +26,7 @@ func newOutput(nft nft.NFT, family family.Type, table string, chain string, defa
baseChain := nftChain.BaseChainOptions{
Type: nftChain.TypeFilter,
Hook: nftChain.HookOutput,
Priority: 0,
Priority: int32(priority),
Policy: policy,
Device: "",
}

View File

@@ -13,6 +13,7 @@ type Config struct {
}
type ConfigOptions struct {
ClearMode ClearMode
SavesRules bool
SavesRulesPath string
DnsStrict bool
@@ -32,8 +33,11 @@ type ConfigPolicy struct {
DefaultAllowOutput bool
DefaultAllowForward bool
InputDrop PolicyDrop
InputPriority int
OutputDrop PolicyDrop
OutputPriority int
ForwardDrop PolicyDrop
ForwardPriority int
}
type PolicyDrop int8
@@ -143,3 +147,10 @@ func (d Direction) String() string {
return fmt.Sprintf("Direction(%d)", d)
}
}
type ClearMode int8
const (
ClearModeGlobal ClearMode = iota + 1
ClearModeOwn
)

View File

@@ -43,6 +43,12 @@ func New(pathNFT string, logger log.Logger, config Config) (API, error) {
func (f *firewall) Reload() error {
f.logger.Debug("Reload nftables rules")
if f.config.Options.ClearMode == ClearModeGlobal {
if err := f.nft.Clear(); err != nil {
return err
}
}
chains, err := chain.NewChains(f.nft, f.config.MetadataNaming.TableName)
if err != nil {
return err
@@ -67,9 +73,20 @@ func (f *firewall) Reload() error {
func (f *firewall) ClearRules() {
f.logger.Debug("Clear nftables rules")
switch f.config.Options.ClearMode {
case ClearModeGlobal:
if err := f.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 {
f.logger.Error(fmt.Sprintf("Failed to clear rules: %s", err))
}
break
}
f.logger.Debug("Clear nftables rules done")
}

View File

@@ -2,7 +2,7 @@ package firewall
func (f *firewall) reloadForward() error {
f.logger.Debug("Reloading forward chain")
err := f.chains.NewForward(f.config.MetadataNaming.ChainForwardName, f.config.Policy.DefaultAllowForward)
err := f.chains.NewForward(f.config.MetadataNaming.ChainForwardName, f.config.Policy.DefaultAllowForward, f.config.Policy.ForwardPriority)
if err != nil {
return err
}

View File

@@ -10,7 +10,7 @@ import (
func (f *firewall) reloadInput() error {
f.logger.Debug("Reloading input chain")
err := f.chains.NewInput(f.config.MetadataNaming.ChainInputName, f.config.Policy.DefaultAllowInput)
err := f.chains.NewInput(f.config.MetadataNaming.ChainInputName, f.config.Policy.DefaultAllowInput, f.config.Policy.InputPriority)
if err != nil {
return err
}

View File

@@ -10,7 +10,7 @@ import (
func (f *firewall) reloadOutput() error {
f.logger.Debug("Reloading output chain")
err := f.chains.NewOutput(f.config.MetadataNaming.ChainOutputName, f.config.Policy.DefaultAllowOutput)
err := f.chains.NewOutput(f.config.MetadataNaming.ChainOutputName, f.config.Policy.DefaultAllowOutput, f.config.Policy.OutputPriority)
if err != nil {
return err
}

View File

@@ -4,10 +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/setting/validate"
)
type options struct {
ClearMode string `mapstructure:"clear_mode"`
SavesRules bool `mapstructure:"saves_rules"`
SavesRulesPath string `mapstructure:"saves_rules_path"`
DnsStrict bool `mapstructure:"dns_strict"`
@@ -17,6 +19,7 @@ type options struct {
func defaultOptions() options {
return options{
ClearMode: "global",
SavesRules: false,
SavesRulesPath: "/etc/nftables.conf",
DnsStrict: false,
@@ -45,3 +48,14 @@ func (o options) ValidateSavesRulesPath() error {
return nil
}
func (o options) ToClearMode() (firewall.ClearMode, error) {
switch o.ClearMode {
case "global":
return firewall.ClearModeGlobal, nil
case "own":
return firewall.ClearModeOwn, nil
}
return firewall.ClearModeGlobal, errors.New("invalid option clear_mode. Must be 'global' or 'own'")
}

View File

@@ -11,8 +11,11 @@ type policy struct {
DefaultAllowOutput bool `mapstructure:"default_allow_output"`
DefaultAllowForward bool `mapstructure:"default_allow_forward"`
InputDrop string `mapstructure:"input_drop"`
InputPriority int `mapstructure:"input_priority"`
OutputDrop string `mapstructure:"output_drop"`
OutputPriority int `mapstructure:"output_priority"`
ForwardDrop string `mapstructure:"forward_drop"`
ForwardPriority int `mapstructure:"forward_priority"`
}
func defaultPolicy() policy {
@@ -21,8 +24,11 @@ func defaultPolicy() policy {
DefaultAllowOutput: false,
DefaultAllowForward: false,
InputDrop: "drop",
InputPriority: -10,
OutputDrop: "reject",
OutputPriority: -10,
ForwardDrop: "drop",
ForwardPriority: -10,
}
}
@@ -47,8 +53,11 @@ func (p policy) ToConfigPolicy() (firewall.ConfigPolicy, error) {
DefaultAllowOutput: p.DefaultAllowOutput,
DefaultAllowForward: p.DefaultAllowForward,
InputDrop: inputDrop,
InputPriority: p.InputPriority,
OutputDrop: outputDrop,
OutputPriority: p.OutputPriority,
ForwardDrop: forwardDrop,
ForwardPriority: p.ForwardPriority,
}, nil
}
@@ -70,12 +79,23 @@ func (p policy) Validate() error {
if err := validateDrop(p.InputDrop, "input_drop"); err != nil {
return err
}
if err := validatePriority(p.InputPriority, "input_priority"); err != nil {
return err
}
if err := validateDrop(p.OutputDrop, "output_drop"); err != nil {
return err
}
if err := validatePriority(p.OutputPriority, "output_priority"); err != nil {
return err
}
if err := validateDrop(p.ForwardDrop, "forward_drop"); err != nil {
return err
}
if err := validatePriority(p.ForwardPriority, "forward_priority"); err != nil {
return err
}
return nil
}
@@ -86,3 +106,10 @@ func validateDrop(drop string, parameterName string) error {
}
return fmt.Errorf("invalid %s. Must be drop or reject", parameterName)
}
func validatePriority(priority int, parameterName string) error {
if priority < -50 || priority > 50 {
return fmt.Errorf("%s must be in range -50-50", parameterName)
}
return nil
}

View File

@@ -36,6 +36,11 @@ func (o *otherSettingsPath) ToFirewallConfig() (firewall.Config, error) {
return firewall.Config{}, err
}
optionClearMode, err := setting.Options.ToClearMode()
if err != nil {
return firewall.Config{}, err
}
return firewall.Config{
InPorts: inPorts,
OutPorts: outPorts,
@@ -55,6 +60,7 @@ func (o *otherSettingsPath) ToFirewallConfig() (firewall.Config, error) {
OutIPs: IPs.OutIP6,
},
Options: firewall.ConfigOptions{
ClearMode: optionClearMode,
SavesRules: setting.Options.SavesRules,
SavesRulesPath: setting.Options.SavesRulesPath,
DnsStrict: setting.Options.DnsStrict,