package firewall import ( "context" "fmt" "os" "os/exec" "time" ) // Apply writes ruleset to a temp file and loads it with `nft -f`, after // first deleting the server's own table (ignoring the error - the table // may not exist yet on first apply). Only ever touches the single table // named by TableName(serverID), never any other nftables state. // Returns combined nft output for display, and an error if the load failed. func Apply(serverID, ruleset string) (string, error) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() // best-effort: drop any previous version of this server's table so // reapplying is idempotent. Error ignored - table may not exist yet. _ = exec.CommandContext(ctx, "nft", "delete", "table", "inet", TableName(serverID)).Run() tmpFile, err := os.CreateTemp("", "wg-ui-multi-fw-*.nft") if err != nil { return "", fmt.Errorf("cannot create temp ruleset file: %w", err) } defer os.Remove(tmpFile.Name()) if _, err := tmpFile.WriteString(ruleset); err != nil { tmpFile.Close() return "", fmt.Errorf("cannot write temp ruleset file: %w", err) } if err := tmpFile.Close(); err != nil { return "", fmt.Errorf("cannot close temp ruleset file: %w", err) } cmd := exec.CommandContext(ctx, "nft", "-f", tmpFile.Name()) out, err := cmd.CombinedOutput() if err != nil { return string(out), fmt.Errorf("nft -f failed: %w", err) } return string(out), nil }