Move helper NAT management into Go
Remove the last shell-owned NAT surface by extracting the iptables logic into a shared Go package and using it from both bangerd and a hidden helper bridge in the CLI. Route customize.sh and interactive.sh through banger internal nat up/down so the remaining shell helpers reuse the same rule logic, resolve the local banger binary explicitly, and tear NAT back down during cleanup. Drop nat.sh from the runtime bundle and docs now that NAT is Go-managed everywhere, and keep coverage aligned with the new shared package and helper command. Validation: go test ./..., bash -n customize.sh interactive.sh verify.sh, make build, and a live ./verify.sh --nat run that installed host rules, reached outbound network access, and cleaned them up successfully.
This commit is contained in:
parent
60294e8c90
commit
430f66d5dd
13 changed files with 378 additions and 250 deletions
|
|
@ -2,46 +2,16 @@ package daemon
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"banger/internal/hostnat"
|
||||
"banger/internal/model"
|
||||
"banger/internal/system"
|
||||
)
|
||||
|
||||
type natRule struct {
|
||||
table string
|
||||
chain string
|
||||
args []string
|
||||
}
|
||||
type natRule = hostnat.Rule
|
||||
|
||||
func (d *Daemon) ensureNAT(ctx context.Context, vm model.VMRecord, enable bool) error {
|
||||
uplink, err := d.validateNATPrereqs(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rules, err := natRulesForVM(vm, uplink)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if enable {
|
||||
if _, err := d.runner.RunSudo(ctx, "sysctl", "-w", "net.ipv4.ip_forward=1"); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, rule := range rules {
|
||||
if err := d.addNATRule(ctx, rule); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
for _, rule := range rules {
|
||||
if err := d.removeNATRule(ctx, rule); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return hostnat.Ensure(ctx, d.runner, vm.Runtime.GuestIP, vm.Runtime.TapDevice, enable)
|
||||
}
|
||||
|
||||
func (d *Daemon) validateNATPrereqs(ctx context.Context) (string, error) {
|
||||
|
|
@ -55,102 +25,29 @@ func (d *Daemon) validateNATPrereqs(ctx context.Context) (string, error) {
|
|||
}
|
||||
|
||||
func (d *Daemon) defaultUplink(ctx context.Context) (string, error) {
|
||||
out, err := d.runner.Run(ctx, "ip", "route", "show", "default")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return parseDefaultUplink(string(out))
|
||||
return hostnat.DefaultUplink(ctx, d.runner)
|
||||
}
|
||||
|
||||
func parseDefaultUplink(output string) (string, error) {
|
||||
for _, line := range strings.Split(output, "\n") {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) == 0 || fields[0] != "default" {
|
||||
continue
|
||||
}
|
||||
for i := 0; i < len(fields)-1; i++ {
|
||||
if fields[i] == "dev" && fields[i+1] != "" {
|
||||
return fields[i+1], nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", errors.New("failed to detect uplink interface")
|
||||
return hostnat.ParseDefaultUplink(output)
|
||||
}
|
||||
|
||||
func natRulesForVM(vm model.VMRecord, uplink string) ([]natRule, error) {
|
||||
guestIP := strings.TrimSpace(vm.Runtime.GuestIP)
|
||||
if guestIP == "" {
|
||||
return nil, errors.New("nat requires a guest IP")
|
||||
}
|
||||
tap := strings.TrimSpace(vm.Runtime.TapDevice)
|
||||
if tap == "" {
|
||||
return nil, errors.New("nat requires a tap device")
|
||||
}
|
||||
uplink = strings.TrimSpace(uplink)
|
||||
if uplink == "" {
|
||||
return nil, errors.New("nat requires an uplink interface")
|
||||
}
|
||||
guestCIDR := guestIP + "/32"
|
||||
return []natRule{
|
||||
{
|
||||
table: "nat",
|
||||
chain: "POSTROUTING",
|
||||
args: []string{"-s", guestCIDR, "-o", uplink, "-j", "MASQUERADE"},
|
||||
},
|
||||
{
|
||||
chain: "FORWARD",
|
||||
args: []string{"-i", tap, "-o", uplink, "-j", "ACCEPT"},
|
||||
},
|
||||
{
|
||||
chain: "FORWARD",
|
||||
args: []string{"-i", uplink, "-o", tap, "-m", "state", "--state", "RELATED,ESTABLISHED", "-j", "ACCEPT"},
|
||||
},
|
||||
}, nil
|
||||
return hostnat.Rules(vm.Runtime.GuestIP, vm.Runtime.TapDevice, uplink)
|
||||
}
|
||||
|
||||
func natRuleArgs(action string, rule natRule) []string {
|
||||
args := make([]string, 0, len(rule.args)+4)
|
||||
if rule.table != "" {
|
||||
args = append(args, "-t", rule.table)
|
||||
}
|
||||
args = append(args, action, rule.chain)
|
||||
args = append(args, rule.args...)
|
||||
return args
|
||||
return hostnat.RuleArgs(action, rule)
|
||||
}
|
||||
|
||||
func natAddPlan(rules []natRule) [][]string {
|
||||
plan := make([][]string, 0, len(rules)+1)
|
||||
plan = append(plan, []string{"sysctl", "-w", "net.ipv4.ip_forward=1"})
|
||||
for _, rule := range rules {
|
||||
plan = append(plan, natRuleArgs("-A", rule))
|
||||
}
|
||||
return plan
|
||||
return hostnat.AddPlan(rules)
|
||||
}
|
||||
|
||||
func natRemovePlan(rules []natRule) [][]string {
|
||||
plan := make([][]string, 0, len(rules))
|
||||
for _, rule := range rules {
|
||||
plan = append(plan, natRuleArgs("-D", rule))
|
||||
}
|
||||
return plan
|
||||
}
|
||||
|
||||
func (d *Daemon) addNATRule(ctx context.Context, rule natRule) error {
|
||||
if _, err := d.runner.RunSudo(ctx, append([]string{"iptables"}, natRuleArgs("-C", rule)...)...); err == nil {
|
||||
return nil
|
||||
}
|
||||
_, err := d.runner.RunSudo(ctx, append([]string{"iptables"}, natRuleArgs("-A", rule)...)...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Daemon) removeNATRule(ctx context.Context, rule natRule) error {
|
||||
if _, err := d.runner.RunSudo(ctx, append([]string{"iptables"}, natRuleArgs("-C", rule)...)...); err != nil {
|
||||
return nil
|
||||
}
|
||||
_, err := d.runner.RunSudo(ctx, append([]string{"iptables"}, natRuleArgs("-D", rule)...)...)
|
||||
return err
|
||||
return hostnat.RemovePlan(rules)
|
||||
}
|
||||
|
||||
func natRuleKey(rule natRule) string {
|
||||
return fmt.Sprintf("%s:%s:%s", rule.table, rule.chain, strings.Join(rule.args, " "))
|
||||
return hostnat.RuleKey(rule)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue