2a3eb3b04d
Spiritual successor to shorewall — manages nftables directly via google/nftables. Reads a single YAML config covering zones, interfaces, hosts, policy, rules, snat, and named portgroups. Computes differential changes against the running nftables state and applies them atomically. Supports detecting and purging rules added outside of tomswall.
80 lines
1.6 KiB
Go
80 lines
1.6 KiB
Go
package nftables
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/google/nftables/expr"
|
|
)
|
|
|
|
type ManagedRule struct {
|
|
Chain string
|
|
Handle uint64
|
|
Exprs []expr.Any
|
|
Tag string
|
|
}
|
|
|
|
type FirewallState struct {
|
|
Rules map[string][]ManagedRule
|
|
}
|
|
|
|
type ChangeSet struct {
|
|
Add []ManagedRule
|
|
Remove []ManagedRule
|
|
}
|
|
|
|
func (cs *ChangeSet) Empty() bool {
|
|
return len(cs.Add) == 0 && len(cs.Remove) == 0
|
|
}
|
|
|
|
func (cs *ChangeSet) Summary() string {
|
|
var b strings.Builder
|
|
if len(cs.Add) > 0 {
|
|
fmt.Fprintf(&b, " + %d rule(s) to add\n", len(cs.Add))
|
|
for _, r := range cs.Add {
|
|
fmt.Fprintf(&b, " + [%s] %s\n", r.Chain, r.Tag)
|
|
}
|
|
}
|
|
if len(cs.Remove) > 0 {
|
|
fmt.Fprintf(&b, " - %d rule(s) to remove\n", len(cs.Remove))
|
|
for _, r := range cs.Remove {
|
|
fmt.Fprintf(&b, " - [%s] %s (handle %d)\n", r.Chain, r.Tag, r.Handle)
|
|
}
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
func computeDiff(current, desired *FirewallState) *ChangeSet {
|
|
cs := &ChangeSet{}
|
|
|
|
currentByTag := make(map[string][]ManagedRule)
|
|
for _, rules := range current.Rules {
|
|
for _, r := range rules {
|
|
if r.Tag != "" {
|
|
currentByTag[r.Tag] = append(currentByTag[r.Tag], r)
|
|
}
|
|
}
|
|
}
|
|
|
|
desiredByTag := make(map[string][]ManagedRule)
|
|
for _, rules := range desired.Rules {
|
|
for _, r := range rules {
|
|
desiredByTag[r.Tag] = append(desiredByTag[r.Tag], r)
|
|
}
|
|
}
|
|
|
|
for tag, desiredRules := range desiredByTag {
|
|
if _, exists := currentByTag[tag]; !exists {
|
|
cs.Add = append(cs.Add, desiredRules...)
|
|
}
|
|
}
|
|
|
|
for tag, currentRules := range currentByTag {
|
|
if _, exists := desiredByTag[tag]; !exists {
|
|
cs.Remove = append(cs.Remove, currentRules...)
|
|
}
|
|
}
|
|
|
|
return cs
|
|
}
|