Files
gyurix c3de398f35
continuous-integration/drone/push Build encountered an error
added network-go project
2026-06-08 15:34:01 +02:00

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
}