136 lines
3.5 KiB
Go
136 lines
3.5 KiB
Go
package config
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
)
|
|
|
|
// NetworksConfig represents the /etc/user/config/networks.json structure
|
|
type NetworksConfig struct {
|
|
Networks map[string]NetworkConfig `json:"networks"`
|
|
IPs map[string]IPConfig `json:"ips"`
|
|
Policies []PolicyConfig `json:"policies"`
|
|
}
|
|
|
|
// NetworkConfig represents a single network definition
|
|
type NetworkConfig struct {
|
|
NetworkName string `json:"network_name"`
|
|
Subnet string `json:"subnet"`
|
|
Gateway string `json:"gateway"`
|
|
}
|
|
|
|
// IPConfig represents a single IP assignment
|
|
type IPConfig struct {
|
|
IP string `json:"ip"`
|
|
ContainerName string `json:"container_name"`
|
|
Selector string `json:"selector"`
|
|
ServiceName string `json:"service_name"`
|
|
}
|
|
|
|
// PolicyConfig represents a single policy rule from networks.json
|
|
type PolicyConfig struct {
|
|
ServiceName string `json:"service_name,omitempty"`
|
|
ContainerName string `json:"container_name,omitempty"`
|
|
Selector string `json:"selector,omitempty"`
|
|
From string `json:"from,omitempty"`
|
|
To string `json:"to,omitempty"`
|
|
Port int `json:"port,omitempty"`
|
|
Proto string `json:"proto,omitempty"`
|
|
Name string `json:"name,omitempty"`
|
|
Iface string `json:"iface,omitempty"`
|
|
Nat string `json:"nat,omitempty"`
|
|
}
|
|
|
|
// FirewallRule is a resolved, executable firewall rule derived from a PolicyConfig
|
|
type FirewallRule struct {
|
|
// Source info
|
|
SourceIP string
|
|
SourcePort int
|
|
SourceIface string
|
|
|
|
// Target info
|
|
TargetIP string
|
|
TargetPort int
|
|
|
|
// Protocol (tcp/udp)
|
|
Proto string
|
|
|
|
// Chain and table
|
|
Chain string // PREROUTING, POSTROUTING, FORWARD, DOCKER-USER, etc.
|
|
Table string // nat, filter
|
|
|
|
// Action
|
|
Action string // DNAT, MASQUERADE, ACCEPT, DROP
|
|
|
|
// Comment for iptables
|
|
Comment string
|
|
|
|
// Namespace info (empty = host, non-empty = container PID namespace)
|
|
ContainerPID string
|
|
}
|
|
|
|
// ToCIDR converts an IP without mask to a /24 CIDR notation (matching shell script behavior)
|
|
func ToCIDR(ip string) string {
|
|
// If it's already a CIDR, return as-is
|
|
if _, _, err := net.ParseCIDR(ip); err == nil {
|
|
return ip
|
|
}
|
|
// If last octet is 0, it's already a network address
|
|
parsed := net.ParseIP(ip)
|
|
if parsed == nil {
|
|
return ip
|
|
}
|
|
ipv4 := parsed.To4()
|
|
if ipv4 == nil {
|
|
return ip
|
|
}
|
|
if ipv4[3] == 0 {
|
|
return fmt.Sprintf("%d.%d.%d.0/24", ipv4[0], ipv4[1], ipv4[2])
|
|
}
|
|
return ip
|
|
}
|
|
|
|
// NetworkPrefix returns the first three octets as a /24 CIDR
|
|
func NetworkPrefix(ip string) string {
|
|
parsed := net.ParseIP(ip)
|
|
if parsed == nil {
|
|
return ip
|
|
}
|
|
ipv4 := parsed.To4()
|
|
if ipv4 == nil {
|
|
return ip
|
|
}
|
|
return fmt.Sprintf("%d.%d.%d.0/24", ipv4[0], ipv4[1], ipv4[2])
|
|
}
|
|
|
|
// IsIP checks if a string is an IPv4 address
|
|
func IsIP(s string) bool {
|
|
parsed := net.ParseIP(s)
|
|
return parsed != nil && parsed.To4() != nil
|
|
}
|
|
|
|
// ParseCIDR parses the subnet string into an IPNet
|
|
func (n NetworkConfig) ParseCIDR() (*net.IPNet, error) {
|
|
_, ipNet, err := net.ParseCIDR(n.Subnet)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse subnet %s: %w", n.Subnet, err)
|
|
}
|
|
return ipNet, nil
|
|
}
|
|
|
|
// Load reads and parses the networks.json configuration file
|
|
func Load(path string) (*NetworksConfig, error) {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read config file %s: %w", path, err)
|
|
}
|
|
|
|
var config NetworksConfig
|
|
if err := json.Unmarshal(data, &config); err != nil {
|
|
return nil, fmt.Errorf("failed to parse config file %s: %w", path, err)
|
|
}
|
|
|
|
return &config, nil
|
|
} |