Add live firewall rule management per server (nftables)
New model.FirewallRule + jsondb CRUD (GetFirewallRules/CreateFirewallRule/ UpdateFirewallRule/DeleteFirewallRule), scoped per server. firewall package now generates a full ruleset (baseline + enabled custom rules) and can apply it live via `nft -f` (firewall.Apply), scoped to a per-server nftables table (wireguard_ui_<serverID>) so applying one server never touches another server's rules or any pre-existing firewall state. New endpoints: GET/POST /servers/:id/firewall/rules, POST .../rules/:ruleId, POST .../rules/:ruleId/delete, POST .../apply (live, admin-only). UI in the All Servers page: rule table with add/delete, ruleset preview, and an "Apply now (live)" button with an explicit confirm() warning before it touches the running firewall. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
28eb08df41
commit
1d080904b0
@@ -677,3 +677,62 @@ func (o *JsonDB) SaveServerHashes(serverID string, hashes model.ClientServerHash
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
// GetFirewallRules func to query all firewall rules belonging to a server
|
||||
func (o *JsonDB) GetFirewallRules(serverID string) ([]model.FirewallRule, error) {
|
||||
if err := validateServerID(serverID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rules := make([]model.FirewallRule, 0)
|
||||
records, err := o.conn.ReadAll("firewall_rules")
|
||||
if err != nil {
|
||||
if err == scribble.ErrMissingCollection {
|
||||
return rules, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
for _, rec := range records {
|
||||
var rule model.FirewallRule
|
||||
if err := json.Unmarshal(rec, &rule); err != nil {
|
||||
return nil, fmt.Errorf("cannot decode firewall rule json structure: %v", err)
|
||||
}
|
||||
if rule.ServerID == serverID {
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
}
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
// CreateFirewallRule func to add a new firewall rule for a server
|
||||
func (o *JsonDB) CreateFirewallRule(rule model.FirewallRule) error {
|
||||
if err := validateServerID(rule.ServerID); err != nil {
|
||||
return err
|
||||
}
|
||||
return o.conn.Write("firewall_rules", rule.ID, rule)
|
||||
}
|
||||
|
||||
// UpdateFirewallRule func to update an existing firewall rule, refusing to
|
||||
// move it to a different server than it currently belongs to
|
||||
func (o *JsonDB) UpdateFirewallRule(rule model.FirewallRule) error {
|
||||
var existing model.FirewallRule
|
||||
if err := o.conn.Read("firewall_rules", rule.ID, &existing); err != nil {
|
||||
return fmt.Errorf("firewall rule not found: %v", err)
|
||||
}
|
||||
if existing.ServerID != rule.ServerID {
|
||||
return fmt.Errorf("cannot change server_id of an existing firewall rule")
|
||||
}
|
||||
return o.conn.Write("firewall_rules", rule.ID, rule)
|
||||
}
|
||||
|
||||
// DeleteFirewallRule func to remove a firewall rule, verifying it belongs to
|
||||
// the given server first
|
||||
func (o *JsonDB) DeleteFirewallRule(serverID, ruleID string) error {
|
||||
var existing model.FirewallRule
|
||||
if err := o.conn.Read("firewall_rules", ruleID, &existing); err != nil {
|
||||
return fmt.Errorf("firewall rule not found: %v", err)
|
||||
}
|
||||
if existing.ServerID != serverID {
|
||||
return fmt.Errorf("firewall rule does not belong to server %s", serverID)
|
||||
}
|
||||
return o.conn.Delete("firewall_rules", ruleID)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user