go-firewalld/properties.go
2026-07-02 14:08:37 -05:00

163 lines
6.1 KiB
Go

package firewalld
import (
"context"
"github.com/godbus/dbus/v5"
)
// This file exposes firewalld's D-Bus properties: read-only runtime state on the
// main object, and the read/write daemon settings (firewalld.conf knobs) on the
// config object. Both are read through org.freedesktop.DBus.Properties.
// getProperty reads a single property via Properties.Get.
func (c *Conn) getProperty(ctx context.Context, path dbus.ObjectPath, iface, name string) (dbus.Variant, error) {
var v dbus.Variant
err := c.call(ctx, path, ifaceProperties, "Get", []any{&v}, iface, name)
return v, err
}
// getAllProperties reads every property of an interface via Properties.GetAll.
func (c *Conn) getAllProperties(ctx context.Context, path dbus.ObjectPath, iface string) (map[string]dbus.Variant, error) {
var m map[string]dbus.Variant
err := c.call(ctx, path, ifaceProperties, "GetAll", []any{&m}, iface)
return m, err
}
// setProperty writes a property via Properties.Set.
func (c *Conn) setProperty(ctx context.Context, path dbus.ObjectPath, iface, name string, value any) error {
return c.call(ctx, path, ifaceProperties, "Set", nil, iface, name, dbus.MakeVariant(value))
}
// State returns firewalld's runtime state, e.g. "RUNNING".
func (c *Conn) State(ctx context.Context) (string, error) {
v, err := c.getProperty(ctx, basePath, ifaceMain, "state")
if err != nil {
return "", err
}
return asString(v.Value()), nil
}
// MainProperty reads a single read-only property from the main object (e.g.
// "IPv4", "IPSetTypes", "nf_conntrack_helpers") for values not covered by a typed
// accessor.
func (c *Conn) MainProperty(ctx context.Context, name string) (dbus.Variant, error) {
return c.getProperty(ctx, basePath, ifaceMain, name)
}
// RuntimeInfo captures the main object's read-only capability and state
// properties, describing what the running firewalld supports.
type RuntimeInfo struct {
State string
InterfaceVer string
IPv4 bool
IPv6 bool
IPv6RPFilter bool
Bridge bool
IPSet bool
IPSetTypes []string
IPv4ICMPTypes []string
IPv6ICMPTypes []string
}
// RuntimeInfo reads the main object's runtime properties in one round-trip.
func (c *Conn) RuntimeInfo(ctx context.Context) (RuntimeInfo, error) {
m, err := c.getAllProperties(ctx, basePath, ifaceMain)
if err != nil {
return RuntimeInfo{}, err
}
get := func(k string) any {
if v, ok := m[k]; ok {
return v.Value()
}
return nil
}
return RuntimeInfo{
State: asString(get("state")),
InterfaceVer: asString(get("interface_version")),
IPv4: asBool(get("IPv4")),
IPv6: asBool(get("IPv6")),
IPv6RPFilter: asBool(get("IPv6_rpfilter")),
Bridge: asBool(get("BRIDGE")),
IPSet: asBool(get("IPSet")),
IPSetTypes: asStrings(get("IPSetTypes")),
IPv4ICMPTypes: asStrings(get("IPv4ICMPTypes")),
IPv6ICMPTypes: asStrings(get("IPv6ICMPTypes")),
}, nil
}
// DaemonConfig holds firewalld's permanent daemon settings (the firewalld.conf
// knobs). Fields absent on the connected version read as empty. Most values are
// firewalld's "yes"/"no" strings or an enum ("iptables"/"nftables"); they are
// exposed verbatim rather than coerced, since firewalld's own semantics vary.
type DaemonConfig struct {
DefaultZone string
MinimalMark int32
CleanupOnExit string
CleanupModulesOnExit string
Lockdown string
IPv6RPFilter string
IPv6RPFilter2 string // finer-grained rp_filter (newer firewalld)
IndividualCalls string
LogDenied string
AutomaticHelpers string
FirewallBackend string
FlushAllOnReload string
RFC3964IPv4 string
AllowZoneDrifting string
NftablesTableOwner string
NftablesCounters string // nftables rule counters (firewalld >= 2.0)
NftablesFlowtable string // nftables flowtable offload (firewalld >= 2.0)
StrictForwardPorts string // strict forward-port handling (firewalld >= 2.0)
}
// DaemonConfig reads the config object's daemon-settings properties.
func (p *Permanent) DaemonConfig(ctx context.Context) (DaemonConfig, error) {
m, err := p.c.getAllProperties(ctx, configPath, ifaceConfig)
if err != nil {
return DaemonConfig{}, err
}
get := func(k string) any {
if v, ok := m[k]; ok {
return v.Value()
}
return nil
}
return DaemonConfig{
DefaultZone: asString(get("DefaultZone")),
MinimalMark: asInt32(get("MinimalMark")),
CleanupOnExit: asString(get("CleanupOnExit")),
CleanupModulesOnExit: asString(get("CleanupModulesOnExit")),
Lockdown: asString(get("Lockdown")),
IPv6RPFilter: asString(get("IPv6_rpfilter")),
IPv6RPFilter2: asString(get("IPv6_rpfilter2")),
IndividualCalls: asString(get("IndividualCalls")),
LogDenied: asString(get("LogDenied")),
AutomaticHelpers: asString(get("AutomaticHelpers")),
FirewallBackend: asString(get("FirewallBackend")),
FlushAllOnReload: asString(get("FlushAllOnReload")),
RFC3964IPv4: asString(get("RFC3964_IPv4")),
AllowZoneDrifting: asString(get("AllowZoneDrifting")),
NftablesTableOwner: asString(get("NftablesTableOwner")),
NftablesCounters: asString(get("NftablesCounters")),
NftablesFlowtable: asString(get("NftablesFlowtable")),
StrictForwardPorts: asString(get("StrictForwardPorts")),
}, nil
}
// ConfigProperty reads a single daemon-config property by its firewalld name
// (e.g. "FirewallBackend", "IPv6_rpfilter").
func (p *Permanent) ConfigProperty(ctx context.Context, name string) (dbus.Variant, error) {
return p.c.getProperty(ctx, configPath, ifaceConfig, name)
}
// SetConfigProperty writes a daemon-config property. Changes are permanent and
// take effect after a reload; most require privilege.
func (p *Permanent) SetConfigProperty(ctx context.Context, name string, value any) error {
return p.c.setProperty(ctx, configPath, ifaceConfig, name, value)
}
// SetFirewallBackend selects the packet backend ("nftables" or "iptables").
func (p *Permanent) SetFirewallBackend(ctx context.Context, backend string) error {
return p.SetConfigProperty(ctx, "FirewallBackend", backend)
}