refactor config settings for new features

Signed-off-by: Steffen Vogel <post@steffenvogel.de>
This commit is contained in:
Steffen Vogel
2022-07-17 02:28:45 +02:00
parent 70460f7f7e
commit 1e4485ef9b
19 changed files with 276 additions and 229 deletions

View File

@@ -7,7 +7,7 @@ backends:
- k8s:///path/to/your/kubeconfig.yaml?namespace=default
# Wireguard settings
wg:
wireguard:
# Use wg / wg-quick configuration files
config:
path: /etc/wireguard
@@ -38,67 +38,71 @@ socket:
# Mostly useful for testing automation
wait: false
# Interactive Connectivity Establishment
ice:
# A list of STUN and TURN servers used by ICE
urls:
- stun:l.google.com:19302
route_sync:
enabled: true
# Credentils for STUN/TURN servers configured above
username: ""
password: ""
table: main
# Allow connections to STUNS/TURNS servers for which
# we cant validate their TLS certificates
insecure_skip_verify: false
config_sync:
enabled: true
# Limit available network and candidate types
network-types: [udp4, udp6, tcp4, tcp6]
candidate_types: [host, srflx, prflx ,relay]
watch: true
# Regular expression whitelist of interfaces which are used to gather ICE candidates.
interface_filter: .*
# Endpoint Discovery
endpoint_disc:
enabled: true
# Lite agents do not perform connectivity check and only provide host candidates.
lite: false
# Interactive Connectivity Establishment
ice:
# A list of STUN and TURN servers used by ICE
urls:
- stun:l.google.com:19302
# Attempt to find candidates via mDNS discovery
mdns: false
# Credentils for STUN/TURN servers configured above
username: ""
password: ""
# Sets the max amount of binding requests the agent will send over a candidate pair for validation or nomination.
# If after the the configured number, the candidate is yet to answer a binding request or a nomination we set the pair as failed.
max_binding_requests: 7
# Allow connections to STUNS/TURNS servers for which
# we cant validate their TLS certificates
insecure_skip_verify: false
# SetNAT1To1IPs sets a list of external IP addresses of 1:1 (D)NAT and a candidate type for which the external IP address is used.
# This is useful when you are host a server using Pion on an AWS EC2 instance which has a private address, behind a 1:1 DNAT with a public IP (e.g. Elastic IP).
# In this case, you can give the public IP address so that Pion will use the public IP address in its candidate instead of the private IP address.
nat_1to1_ips: []
# Limit available network and candidate types
network-types: [udp4, udp6, tcp4, tcp6]
candidate_types: [host, srflx, prflx ,relay]
# Limit the port range used by ICE
port:
min: 49152
max: 65535
# Regular expression whitelist of interfaces which are used to gather ICE candidates.
interface_filter: .*
# The check interval controls how often our task loop runs when in the connecting state.
check_interval: 200ms
# If the duration is 0, the ICE Agent will never go to disconnected
disconnected_timeout: 5s
# Lite agents do not perform connectivity check and only provide host candidates.
lite: false
# If the duration is 0, we will never go to failed.
failed_timeout: 5s
restart_timeout: 5s
# Attempt to find candidates via mDNS discovery
mdns: false
# Determines how often should we send ICE keepalives (should be less then connection timeout above).
# A keepalive interval of 0 means we never send keepalive packets
keepalive_interval: 2s
# Sets the max amount of binding requests the agent will send over a candidate pair for validation or nomination.
# If after the the configured number, the candidate is yet to answer a binding request or a nomination we set the pair as failed.
max_binding_requests: 7
# Settings for forwarding / proxying encapsulated Wireguard traffic between
# pion/ice and the Kernel Wireguard interfaces
proxy:
# Use NFtables to setup a port redirect / NAT for server reflexive candidates
nft: true
# SetNAT1To1IPs sets a list of external IP addresses of 1:1 (D)NAT and a candidate type for which the external IP address is used.
# This is useful when you are host a server using Pion on an AWS EC2 instance which has a private address, behind a 1:1 DNAT with a public IP (e.g. Elastic IP).
# In this case, you can give the public IP address so that Pion will use the public IP address in its candidate instead of the private IP address.
nat_1to1_ips: []
# Use a RAW socket with an attached eBPF socket filter to receive STUN packets while
# all other data is directly received by the ListenPort of a kernel-space Wireguard interface.
ebpf: true
# Limit the port range used by ICE
port:
min: 49152
max: 65535
# The check interval controls how often our task loop runs when in the connecting state.
check_interval: 200ms
# If the duration is 0, the ICE Agent will never go to disconnected
disconnected_timeout: 5s
# If the duration is 0, we will never go to failed.
failed_timeout: 5s
restart_timeout: 5s
# Determines how often should we send ICE keepalives (should be less then connection timeout above).
# A keepalive interval of 0 means we never send keepalive packets
keepalive_interval: 2s

View File

@@ -6,69 +6,69 @@ import (
func (c *Config) AgentConfig() (*ice.AgentConfig, error) {
cfg := &ice.AgentConfig{
InsecureSkipVerify: c.ICE.InsecureSkipVerify,
Lite: c.ICE.Lite,
PortMin: uint16(c.ICE.Port.Min),
PortMax: uint16(c.ICE.Port.Max),
InsecureSkipVerify: c.EndpointDisc.ICE.InsecureSkipVerify,
Lite: c.EndpointDisc.ICE.Lite,
PortMin: uint16(c.EndpointDisc.ICE.Port.Min),
PortMax: uint16(c.EndpointDisc.ICE.Port.Max),
}
cfg.InterfaceFilter = func(name string) bool {
return c.ICE.InterfaceFilter.MatchString(name)
return c.EndpointDisc.ICE.InterfaceFilter.MatchString(name)
}
// ICE URLs
cfg.Urls = []*ice.URL{}
for _, u := range c.ICE.URLs {
for _, u := range c.EndpointDisc.ICE.URLs {
p := &u.URL
// Set ICE credentials for TURN/TURNS servers
if p.Scheme == ice.SchemeTypeTURN || p.Scheme == ice.SchemeTypeTURNS {
p.Username = c.ICE.Username
p.Password = c.ICE.Password
p.Username = c.EndpointDisc.ICE.Username
p.Password = c.EndpointDisc.ICE.Password
}
cfg.Urls = append(cfg.Urls, p)
}
if len(c.ICE.NAT1to1IPs) > 0 {
cfg.NAT1To1IPs = c.ICE.NAT1to1IPs
if len(c.EndpointDisc.ICE.NAT1to1IPs) > 0 {
cfg.NAT1To1IPs = c.EndpointDisc.ICE.NAT1to1IPs
}
if mbr := uint16(c.ICE.MaxBindingRequests); mbr > 0 {
if mbr := uint16(c.EndpointDisc.ICE.MaxBindingRequests); mbr > 0 {
cfg.MaxBindingRequests = &mbr
}
if c.ICE.MDNS {
if c.EndpointDisc.ICE.MDNS {
cfg.MulticastDNSMode = ice.MulticastDNSModeQueryAndGather
}
if to := c.ICE.DisconnectedTimeout; to > 0 {
if to := c.EndpointDisc.ICE.DisconnectedTimeout; to > 0 {
cfg.DisconnectedTimeout = &to
}
if to := c.ICE.FailedTimeout; to > 0 {
if to := c.EndpointDisc.ICE.FailedTimeout; to > 0 {
cfg.FailedTimeout = &to
}
if to := c.ICE.KeepaliveInterval; to > 0 {
if to := c.EndpointDisc.ICE.KeepaliveInterval; to > 0 {
cfg.KeepaliveInterval = &to
}
if to := c.ICE.CheckInterval; to > 0 {
if to := c.EndpointDisc.ICE.CheckInterval; to > 0 {
cfg.CheckInterval = &to
}
if len(c.ICE.CandidateTypes) > 0 {
if len(c.EndpointDisc.ICE.CandidateTypes) > 0 {
cfg.CandidateTypes = []ice.CandidateType{}
for _, t := range c.ICE.CandidateTypes {
for _, t := range c.EndpointDisc.ICE.CandidateTypes {
cfg.CandidateTypes = append(cfg.CandidateTypes, t.CandidateType)
}
}
if len(c.ICE.NetworkTypes) > 0 {
if len(c.EndpointDisc.ICE.NetworkTypes) > 0 {
cfg.NetworkTypes = []ice.NetworkType{}
for _, t := range c.ICE.NetworkTypes {
for _, t := range c.EndpointDisc.ICE.NetworkTypes {
cfg.NetworkTypes = append(cfg.NetworkTypes, t.NetworkType)
}
} else {

View File

@@ -79,37 +79,53 @@ func NewConfig(flags *pflag.FlagSet) *Config {
c.SetDefault("watch_interval", "1s")
c.SetDefault("socket.path", DefaultSocketPath)
c.SetDefault("socket.wait", false)
c.SetDefault("wg.config.path", "/etc/wireguard")
c.SetDefault("wg.config.sync", false)
c.SetDefault("wg.config.watch", false)
c.SetDefault("wg.routes.sync", false)
c.SetDefault("wg.routes.table", "main")
c.SetDefault("ice.check_interval", "200ms")
c.SetDefault("ice.keepalive_interval", "2s")
c.SetDefault("ice.disconnected_timeout", "5s")
c.SetDefault("ice.restart_timeout", "5s")
c.SetDefault("ice.failed_timeout", "5s")
c.SetDefault("ice.max_binding_requests", 7)
c.SetDefault("ice.urls", []string{DefaultURL})
c.SetDefault("ice.port.min", EphemeralPortMin)
c.SetDefault("ice.port.max", EphemeralPortMax)
c.SetDefault("auto_config.enabled", true)
c.SetDefault("config_sync.enabled", false)
c.SetDefault("config_sync.path", "/etc/wireguard")
c.SetDefault("config_sync.watch", false)
c.SetDefault("route_sync.enabled", false)
c.SetDefault("route_sync.table", "main")
c.SetDefault("endpoint_disc.ice.enabled", true)
c.SetDefault("endpoint_disc.ice.check_interval", "200ms")
c.SetDefault("endpoint_disc.ice.keepalive_interval", "2s")
c.SetDefault("endpoint_disc.ice.disconnected_timeout", "5s")
c.SetDefault("endpoint_disc.ice.restart_timeout", "5s")
c.SetDefault("endpoint_disc.ice.failed_timeout", "5s")
c.SetDefault("endpoint_disc.ice.max_binding_requests", 7)
c.SetDefault("endpoint_disc.ice.urls", []string{DefaultURL})
c.SetDefault("endpoint_disc.ice.port.min", EphemeralPortMin)
c.SetDefault("endpoint_disc.ice.port.max", EphemeralPortMax)
// Feature flags
flags.BoolP("config-sync", "C", true, "Enable synchronization on-disk Wireguard configuration files")
flags.BoolP("endpoint-disc", "I", true, "Enable ICE endpoint discovery")
flags.BoolP("route-sync", "R", true, "Enable synchronization of AllowedIPs and Kernel routing table")
flags.BoolP("auto-config", "S", true, "Enable setup of link-local addresses")
// Config flags
flags.StringVarP(&c.Domain, "domain", "A", "", "A DNS `domain` name used for DNS auto-configuration")
flags.StringSliceVarP(&c.ConfigFiles, "config", "c", []string{}, "One or more `filename`s of configuration files")
flags.StringP("community", "x", "", "A community `passphrase` for discovering other peers")
// Daemon flags
flags.StringSliceP("backend", "b", []string{}, "One or more `URL`s to signaling backends")
flags.DurationP("watch-interval", "i", 0, "An interval at which we are periodically polling the kernel for updates on Wireguard interfaces")
// Socket
flags.StringP("socket", "s", "", "The `path` of the unix socket used by other wice commands")
flags.Bool("socket-wait", false, "Wait until first client connected to control socket before continuing start")
// Wireguard
flags.StringP("wg-interface-filter", "f", ".*", "A `regex` for filtering Wireguard interfaces (e.g. \"wg-.*\")")
flags.BoolP("wg-userspace", "u", false, "Create new interfaces with userspace Wireguard implementation")
flags.BoolP("wg-config-sync", "S", false, "Synchronize Wireguard interface with configuration file (see \"wg syncconf\")")
flags.StringP("wg-config-path", "w", "", "The `directory` of Wireguard wg/wg-quick configuration files")
flags.BoolP("wg-config-watch", "W", false, "Watch and synchronize changes to the Wireguard configuration files")
flags.BoolP("wg-routes-sync", "R", false, "Synchronize Wireguard AllowedIPs with kernel routing table")
flags.StringP("wg-routes-table", "T", "main", "Kernel routing table to use")
// ice.AgentConfig fields
// Config sync
flags.StringP("config-path", "w", "", "The `directory` of Wireguard wg/wg-quick configuration files")
flags.BoolP("config-watch", "W", false, "Watch and synchronize changes to the Wireguard configuration files")
// Route sync
flags.StringP("route-table", "T", "main", "Kernel routing table to use")
// Endpoint discovery
flags.StringSliceP("url", "a", []string{}, "One or more `URL`s of STUN and/or TURN servers")
flags.StringP("username", "U", "", "The `username` for STUN/TURN credentials")
flags.StringP("password", "P", "", "The `password` for STUN/TURN credentials")
@@ -131,20 +147,34 @@ func NewConfig(flags *pflag.FlagSet) *Config {
flags.Duration("ice-check-interval", 0, "Interval at which the agent performs candidate checks in the connecting phase")
flags.Duration("ice-restart-timeout", 0, "Time to wait before ICE restart")
flags.StringP("socket", "s", "", "The `path` of the unix socket used by other wice commands")
flags.Bool("socket-wait", false, "Wait until first client connected to control socket before continuing start")
// Peer discovery
flags.StringP("community", "x", "", "A community `passphrase` for discovering other peers")
flagMap := map[string]string{
"community": "community",
"backend": "backends",
"watch-interval": "watch_interval",
"wg-userspace": "wg.userspace",
"wg-interface-filter": "wg.interface_filter",
"wg-config-sync": "wg.config.sync",
"wg-config-path": "wg.config.path",
"wg-config-watch": "wg.config.watch",
"wg-routes-sync": "wg.routes.sync",
"wg-routes-table": "wg.routes.table",
// Config sync
"config-sync": "config_sync.enabled",
"config-path": "config_sync.path",
"config-watch": "config_sync.watch",
// Route sync
"route-sync": "route_sync.enabled",
"route-table": "route_sync.table",
"setup": "setup.enabled",
"backend": "backends",
"watch-interval": "watch_interval",
// Socket
"socket": "socket.path",
"socket-wait": "socket.wait",
// Wireguard
"wg-userspace": "wg.userspace",
"wg-interface-filter": "wg.interface_filter",
// Endpoint discovery
"endpoint-disc": "endpoint_disc.enabled",
"url": "ice.urls",
"username": "ice.username",
"password": "ice.password",
@@ -163,18 +193,23 @@ func NewConfig(flags *pflag.FlagSet) *Config {
"ice-keepalive-interval": "ice.keepalive_interval",
"ice-check-interval": "ice.check_interval",
"ice-restart-timeout": "ice.restart_timeout",
"socket": "socket.path",
"socket-wait": "socket.wait",
// Peer discovery
"peer-disc": "peer_disc.enabled",
"community": "community",
}
showAdvancedFlags := os.Getenv("WICE_ADVANCED_CLI") != ""
advancedFlags := map[string]bool{
"config-sync": true,
"route-sync": true,
"endpoint-disc": true,
"peer-disc": true,
"auto-config": true,
"watch-interval": true,
"wg-config-sync": true,
"wg-config-path": true,
"wg-config-watch": true,
"wg-routes-sync": true,
"wg-routes-table": true,
"wg-route-table": true,
"ice-candidate-type": true,
"ice-network-type": true,
"ice-nat-1to1-ip": true,

View File

@@ -26,33 +26,33 @@ var _ = test.SetupLogging()
var _ = Describe("parse command line arguments", func() {
It("can parse a boolean argument like wg-userspace", func() {
cfg, err := config.ParseArgs("--wg-userspace")
c, err := config.ParseArgs("--wg-userspace")
Expect(err).To(Succeed())
Expect(cfg.Wireguard.Userspace).To(BeTrue())
Expect(c.Wireguard.Userspace).To(BeTrue())
})
It("can parse multiple backends", func() {
cfg, err := config.ParseArgs("--backend", "k8s", "--backend", "p2p")
c, err := config.ParseArgs("--backend", "k8s", "--backend", "p2p")
Expect(err).To(Succeed())
Expect(cfg.Backends).To(HaveLen(2))
Expect(cfg.Backends[0].Scheme).To(Equal("k8s"))
Expect(cfg.Backends[1].Scheme).To(Equal("p2p"))
Expect(c.Backends).To(HaveLen(2))
Expect(c.Backends[0].Scheme).To(Equal("k8s"))
Expect(c.Backends[1].Scheme).To(Equal("p2p"))
})
It("can parse a duration value", func() {
cfg, err := config.ParseArgs("--ice-restart-timeout", "10s")
c, err := config.ParseArgs("--ice-restart-timeout", "10s")
Expect(err).To(Succeed())
Expect(cfg.ICE.RestartTimeout).To(Equal(10 * time.Second))
Expect(c.EndpointDisc.ICE.RestartTimeout).To(Equal(10 * time.Second))
})
It("parse an interface list", func() {
cfg, err := config.ParseArgs("wg0", "wg1")
c, err := config.ParseArgs("wg0", "wg1")
Expect(err).To(Succeed())
Expect(cfg.Wireguard.Interfaces).To(ConsistOf("wg0", "wg1"))
Expect(c.Wireguard.Interfaces).To(ConsistOf("wg0", "wg1"))
})
It("fails on invalid arguments", func() {
@@ -98,19 +98,19 @@ var _ = Describe("parse command line arguments", func() {
Expect(cfgFile.WriteString("watch_interval: 1337s\n")).To(BeNumerically(">", 0))
Expect(cfgFile.Close()).To(Succeed())
cfg, err := config.ParseArgs("--config", cfgFile.Name())
c, err := config.ParseArgs("--config", cfgFile.Name())
Expect(err).To(Succeed())
Expect(cfg.WatchInterval).To(Equal(1337 * time.Second))
Expect(c.WatchInterval).To(Equal(1337 * time.Second))
})
Specify("that command line arguments take precedence over settings provided by configuration files", func() {
Expect(cfgFile.WriteString("watch_interval: 1337s\n")).To(BeNumerically(">", 0))
Expect(cfgFile.Close()).To(Succeed())
cfg, err := config.ParseArgs("--config", cfgFile.Name(), "--watch-interval", "1m")
c, err := config.ParseArgs("--config", cfgFile.Name(), "--watch-interval", "1m")
Expect(err).To(Succeed())
Expect(cfg.WatchInterval).To(Equal(time.Minute))
Expect(c.WatchInterval).To(Equal(time.Minute))
})
})
@@ -125,10 +125,10 @@ var _ = Describe("parse command line arguments", func() {
Expect(cfgFile.WriteString(`{ "watch_interval": "1337s" }`)).To(BeNumerically(">", 0))
Expect(cfgFile.Close()).To(Succeed())
cfg, err := config.ParseArgs("--config", cfgFile.Name())
c, err := config.ParseArgs("--config", cfgFile.Name())
Expect(err).To(Succeed())
Expect(cfg.WatchInterval).To(Equal(1337 * time.Second))
Expect(c.WatchInterval).To(Equal(1337 * time.Second))
})
})
@@ -195,69 +195,69 @@ var _ = Describe("use environment variables", func() {
})
It("accepts settings via environment variables", func() {
cfg, err := config.ParseArgs()
c, err := config.ParseArgs()
Expect(err).To(Succeed())
Expect(cfg.ICE.CandidateTypes).To(ConsistOf(
Expect(c.EndpointDisc.ICE.CandidateTypes).To(ConsistOf(
icex.CandidateType{CandidateType: ice.CandidateTypeServerReflexive},
icex.CandidateType{CandidateType: ice.CandidateTypeRelay},
))
})
It("environment variables are overwritten by command line arguments", func() {
cfg, err := config.ParseArgs("--ice-candidate-type", "host")
c, err := config.ParseArgs("--ice-candidate-type", "host")
Expect(err).To(Succeed())
Expect(cfg.ICE.CandidateTypes).To(ConsistOf(
Expect(c.EndpointDisc.ICE.CandidateTypes).To(ConsistOf(
icex.CandidateType{CandidateType: ice.CandidateTypeHost},
))
})
})
var _ = Describe("use proper default options", func() {
var cfg *config.Config
var c *config.Config
BeforeEach(func() {
var err error
cfg, err = config.ParseArgs()
c, err = config.ParseArgs()
Expect(err).To(Succeed())
})
It("should have a default STUN URL", func() {
Expect(cfg.ICE.URLs).To(HaveLen(1))
Expect(cfg.ICE.URLs).To(ContainElement(HaveField("Host", "l.google.com")))
Expect(c.EndpointDisc.ICE.URLs).To(HaveLen(1))
Expect(c.EndpointDisc.ICE.URLs).To(ContainElement(HaveField("Host", "l.google.com")))
})
})
var _ = Describe("dump", func() {
var cfg, cfg2 *config.Config
var c1, c2 *config.Config
BeforeEach(func() {
var err error
cfg, err = config.ParseArgs("--ice-network-type", "udp4,udp6", "--url", "stun:0l.de", "wg0")
c1, err = config.ParseArgs("--ice-network-type", "udp4,udp6", "--url", "stun:0l.de", "wg0")
Expect(err).To(Succeed())
buf := &bytes.Buffer{}
Expect(cfg.Dump(buf)).To(Succeed())
Expect(c1.Dump(buf)).To(Succeed())
cfg2 = config.NewConfig(nil)
cfg2.SetConfigType("yaml")
c2 = config.NewConfig(nil)
c2.SetConfigType("yaml")
Expect(cfg2.MergeConfig(buf)).To(Succeed())
Expect(cfg2.Load()).To(Succeed())
Expect(c2.MergeConfig(buf)).To(Succeed())
Expect(c2.Load()).To(Succeed())
})
It("have equal Wireguard interface lists", func() {
Expect(cfg.Wireguard.Interfaces).To(Equal(cfg2.Wireguard.Interfaces))
Expect(c1.Wireguard.Interfaces).To(Equal(c2.Wireguard.Interfaces))
})
It("have equal ICE network types", func() {
Expect(cfg.ICE.NetworkTypes).To(Equal(cfg2.ICE.NetworkTypes))
Expect(c1.EndpointDisc.ICE.NetworkTypes).To(Equal(c2.EndpointDisc.ICE.NetworkTypes))
})
It("have equal ICE URLs", func() {
Expect(cfg.ICE.URLs).To(Equal(cfg2.ICE.URLs))
Expect(c1.EndpointDisc.ICE.URLs).To(Equal(c2.EndpointDisc.ICE.URLs))
})
})

View File

@@ -115,26 +115,26 @@ var _ = Describe("lookup", func() {
})
It("can do DNS autoconfiguration", Label("dns-autoconfig"), func() {
cfg, err := config.ParseArgs("--domain", "example.com")
c, err := config.ParseArgs("--domain", "example.com")
Expect(err).To(Succeed())
Expect(cfg.Community).To(Equal("my-community-password"))
Expect(cfg.ICE.Username).To(Equal("user1"))
Expect(cfg.ICE.Password).To(Equal("pass1"))
Expect(cfg.Backends).To(ConsistOf(
Expect(c.Community).To(Equal("my-community-password"))
Expect(c.EndpointDisc.ICE.Username).To(Equal("user1"))
Expect(c.EndpointDisc.ICE.Password).To(Equal("pass1"))
Expect(c.Backends).To(ConsistOf(
config.BackendURL{URL: url.URL{Scheme: "p2p"}},
config.BackendURL{URL: url.URL{Scheme: "grpc", Host: "example.com:8080"}},
))
Expect(cfg.ICE.URLs).To(ConsistOf(
Expect(c.EndpointDisc.ICE.URLs).To(ConsistOf(
icex.URL{URL: ice.URL{Scheme: ice.SchemeTypeSTUN, Host: "stun.example.com.", Port: 3478, Proto: ice.ProtoTypeUDP}},
icex.URL{URL: ice.URL{Scheme: ice.SchemeTypeTURN, Host: "turn.example.com.", Port: 3478, Proto: ice.ProtoTypeUDP}},
icex.URL{URL: ice.URL{Scheme: ice.SchemeTypeSTUNS, Host: "stun.example.com.", Port: 3478, Proto: ice.ProtoTypeTCP}},
icex.URL{URL: ice.URL{Scheme: ice.SchemeTypeTURNS, Host: "turn.example.com.", Port: 5349, Proto: ice.ProtoTypeTCP}},
icex.URL{URL: ice.URL{Scheme: ice.SchemeTypeTURN, Host: "turn.example.com.", Port: 3478, Proto: ice.ProtoTypeTCP}},
))
Expect(cfg.Wireguard.Interfaces).To(ContainElement("wg-test"))
Expect(c.Wireguard.Interfaces).To(ContainElement("wg-test"))
cfg.Dump(GinkgoWriter)
c.Dump(GinkgoWriter)
})
AfterEach(func() {

View File

@@ -48,35 +48,45 @@ type SocketSettings struct {
Wait bool `yaml:"wait,omitempty"`
}
type WireguardConfigSettings struct {
Path string `yaml:"path,omitempty"`
Sync bool `yaml:"sync,omitempty"`
Watch bool `yaml:"watch,omitempty"`
type ConfigSyncSettings struct {
Enabled bool `yaml:"enabled,omitempty"`
Path string `yaml:"path,omitempty"`
Watch bool `yaml:"watch,omitempty"`
}
type WireguardRoutesSettings struct {
Sync bool `yaml:"sync,omitempty"`
Table string `yaml:"table,omitempty"`
type RouteSyncSettings struct {
Enabled bool `yaml:"enabled,omitempty"`
Table string `yaml:"table,omitempty"`
}
type WireguardSettings struct {
Config WireguardConfigSettings `yaml:"config,omitempty"`
Routes WireguardRoutesSettings `yaml:"routes,omitempty"`
Userspace bool `yaml:"userspace,omitempty"`
InterfaceFilter Regexp `yaml:"interface_filter,omitempty"`
Interfaces []string `yaml:"interfaces,omitempty"`
}
type AutoConfigSettings struct {
Enabled bool `yaml:"enabled,omitempty"`
}
type EndpointDiscoverySettings struct {
Enabled bool `yaml:"enabled,omitempty"`
ICE ICESettings `yaml:"ice,omitempty"`
}
type Settings struct {
Community string `yaml:"community,omitempty"`
WatchInterval time.Duration `yaml:"watch_interval,omitempty"`
Backends []BackendURL `yaml:"backends,omitempty"`
ICE ICESettings `yaml:"ice,omitempty"`
Socket SocketSettings `yaml:"socket,omitempty"`
Wireguard WireguardSettings `yaml:"wg,omitempty"`
Socket SocketSettings `yaml:"socket,omitempty"`
Wireguard WireguardSettings `yaml:"wireguard,omitempty"`
AutoConfig AutoConfigSettings `yaml:"auto_config,omitempty"`
ConfigSync ConfigSyncSettings `yaml:"config_sync,omitempty"`
RouteSync RouteSyncSettings `yaml:"route_sync,omitempty"`
EndpointDisc EndpointDiscoverySettings `yaml:"endpoint_disc,omitempty"`
}
func (s *Settings) Dump(wr io.Writer) error {

View File

@@ -16,10 +16,10 @@ import (
"riasc.eu/wice/internal/wg"
"riasc.eu/wice/pkg/core"
"riasc.eu/wice/pkg/device"
"riasc.eu/wice/pkg/feat/disc/ice"
"riasc.eu/wice/pkg/feat/setup"
config_sync "riasc.eu/wice/pkg/feat/sync/config"
route_sync "riasc.eu/wice/pkg/feat/sync/routes"
ac "riasc.eu/wice/pkg/feat/auto"
ep "riasc.eu/wice/pkg/feat/disc/ep"
cs "riasc.eu/wice/pkg/feat/sync/config"
rs "riasc.eu/wice/pkg/feat/sync/routes"
"riasc.eu/wice/pkg/watcher"
"riasc.eu/wice/pkg/signaling"
@@ -32,10 +32,10 @@ type Daemon struct {
// Features
ConfigSyncer *config_sync.Syncer
RouteSyncer *route_sync.Syncer
Setup *setup.Setup
EndpointDiscovery *ice.EndpointDiscovery
AutoConfig *ac.AutoConfiguration
ConfigSync *cs.ConfigSynchronization
RouteSync *rs.RouteSynchronization
EndpointDiscovery *ep.EndpointDiscovery
// Shared
@@ -114,17 +114,16 @@ func NewDaemon(cfg *config.Config) (*Daemon, error) {
func (d *Daemon) setupFeatures() error {
var err error
// TODO: Add configuration setting
if true {
if d.Setup, err = setup.New(d.Watcher, d.client); err != nil {
return fmt.Errorf("failed to create interface setuper: %w", err)
if d.config.AutoConfig.Enabled {
if d.AutoConfig, err = ac.New(d.Watcher, d.client); err != nil {
return fmt.Errorf("failed to start interface auto configuration: %w", err)
}
}
if d.config.Wireguard.Config.Sync {
if d.ConfigSyncer, err = config_sync.New(d.Watcher, d.client,
d.config.Wireguard.Config.Path,
d.config.Wireguard.Config.Watch,
if d.config.ConfigSync.Enabled {
if d.ConfigSync, err = cs.New(d.Watcher, d.client,
d.config.ConfigSync.Path,
d.config.ConfigSync.Watch,
d.config.Wireguard.Userspace); err != nil {
return fmt.Errorf("failed to start configuration file synchronization: %w", err)
@@ -133,17 +132,16 @@ func (d *Daemon) setupFeatures() error {
d.logger.Info("Started configuration file synchronization")
}
if d.config.Wireguard.Routes.Sync {
if d.RouteSyncer, err = route_sync.New(d.Watcher, d.config.Wireguard.Routes.Table); err != nil {
if d.config.RouteSync.Enabled {
if d.RouteSync, err = rs.New(d.Watcher, d.config.RouteSync.Table); err != nil {
return fmt.Errorf("failed to start allowed-ips <-> kernel route synchronization: %w", err)
}
d.logger.Info("Started allowed-ips <-> kernel route synchronization")
}
// TODO: Add configuration setting
if true {
if d.EndpointDiscovery, err = ice.New(d.Watcher, d.config, d.client, d.Backend); err != nil {
if d.config.EndpointDisc.Enabled {
if d.EndpointDiscovery, err = ep.New(d.Watcher, d.config, d.client, d.Backend); err != nil {
return fmt.Errorf("failed to start endpoint discovery: %w", err)
}

View File

@@ -1,4 +1,4 @@
package setup
package auto
import (
"errors"
@@ -18,7 +18,7 @@ import (
"riasc.eu/wice/pkg/watcher"
)
type Setup struct {
type AutoConfiguration struct {
client *wgctrl.Client
logger *zap.Logger
@@ -52,8 +52,8 @@ func deleteLinkLocalAddresses(dev device.KernelDevice, pk crypto.Key) error {
return nil
}
func New(w *watcher.Watcher, client *wgctrl.Client) (*Setup, error) {
s := &Setup{
func New(w *watcher.Watcher, client *wgctrl.Client) (*AutoConfiguration, error) {
s := &AutoConfiguration{
client: client,
logger: zap.L().Named("setup"),
}
@@ -63,10 +63,10 @@ func New(w *watcher.Watcher, client *wgctrl.Client) (*Setup, error) {
return s, nil
}
func (s *Setup) OnInterfaceAdded(i *core.Interface) {
func (s *AutoConfiguration) OnInterfaceAdded(i *core.Interface) {
logger := s.logger.With(zap.String("intf", i.Name()))
i.OnPeer.Register(s)
i.OnPeer(s)
if err := s.fixupInterface(i); err != nil {
logger.Error("Failed to fix interface", zap.Error(err))
@@ -83,9 +83,9 @@ func (s *Setup) OnInterfaceAdded(i *core.Interface) {
}
}
func (s *Setup) OnInterfaceRemoved(i *core.Interface) {}
func (s *AutoConfiguration) OnInterfaceRemoved(i *core.Interface) {}
func (s *Setup) OnInterfaceModified(i *core.Interface, old *wg.Device, mod core.InterfaceModifier) {
func (s *AutoConfiguration) OnInterfaceModified(i *core.Interface, old *wg.Device, mod core.InterfaceModifier) {
// Update link-local addresses in case the interface key has changed
if mod&core.InterfaceModifiedPrivateKey != 0 {
@@ -102,7 +102,7 @@ func (s *Setup) OnInterfaceModified(i *core.Interface, old *wg.Device, mod core.
}
}
func (s *Setup) OnPeerAdded(p *core.Peer) {
func (s *AutoConfiguration) OnPeerAdded(p *core.Peer) {
logger := s.logger.With(
zap.String("intf", p.Interface.Name()),
zap.Any("peer", p.PublicKey()))
@@ -123,14 +123,14 @@ func (s *Setup) OnPeerAdded(p *core.Peer) {
}
}
func (s *Setup) OnPeerRemoved(p *core.Peer) {}
func (s *AutoConfiguration) OnPeerRemoved(p *core.Peer) {}
func (s *Setup) OnPeerModified(p *core.Peer, old *wgtypes.Peer, mod core.PeerModifier, ipsAdded, ipsRemoved []net.IPNet) {
func (s *AutoConfiguration) OnPeerModified(p *core.Peer, old *wgtypes.Peer, mod core.PeerModifier, ipsAdded, ipsRemoved []net.IPNet) {
}
// fixupInterface fixes the Wireguard device configuration by applying missing settings
func (s *Setup) fixupInterface(i *core.Interface) error {
func (s *AutoConfiguration) fixupInterface(i *core.Interface) error {
cfg := wgtypes.Config{}
logger := s.logger.With(zap.String("intf", i.Name()))

View File

@@ -1,3 +1,3 @@
// This package handles initial configuration of new interfaces and peers
package setup
package auto

View File

@@ -1,3 +1,3 @@
// This package implements the endpoint discovery feature using Interactive Connection Establishment (ICE).
package ice
package ep

View File

@@ -1,4 +1,4 @@
package ice
package ep
import (
"net"

View File

@@ -1,4 +1,4 @@
package ice
package ep
import "github.com/pion/ice/v2"

View File

@@ -1,4 +1,4 @@
package ice
package ep
import (
"fmt"

View File

@@ -1,4 +1,4 @@
package ice
package ep
import (
"context"

View File

@@ -1,4 +1,4 @@
package ice
package ep
import (
"fmt"

View File

@@ -14,8 +14,8 @@ import (
"riasc.eu/wice/pkg/watcher"
)
// Syncer synchronizes the Wireguard device configuration with an on-disk configuration file.
type Syncer struct {
// ConfigSynchronization synchronizes the Wireguard device configuration with an on-disk configuration file.
type ConfigSynchronization struct {
watcher *watcher.Watcher
client *wgctrl.Client
@@ -27,8 +27,8 @@ type Syncer struct {
}
// New creates a new Syncer
func New(w *watcher.Watcher, client *wgctrl.Client, cfgPath string, watch bool, user bool) (*Syncer, error) {
s := &Syncer{
func New(w *watcher.Watcher, client *wgctrl.Client, cfgPath string, watch bool, user bool) (*ConfigSynchronization, error) {
s := &ConfigSynchronization{
watcher: w,
client: client,
cfgPath: cfgPath,
@@ -36,7 +36,7 @@ func New(w *watcher.Watcher, client *wgctrl.Client, cfgPath string, watch bool,
logger: zap.L().Named("sync.config"),
}
w.OnInterface.Register(s)
w.OnInterface(s)
if watch {
go s.watch()
@@ -46,7 +46,7 @@ func New(w *watcher.Watcher, client *wgctrl.Client, cfgPath string, watch bool,
}
// OnInterfaceAdded is a handler which is called whenever an interface has been added
func (s *Syncer) OnInterfaceAdded(i *core.Interface) {
func (s *ConfigSynchronization) OnInterfaceAdded(i *core.Interface) {
cfg := path.Join(s.cfgPath, fmt.Sprintf("%s.conf", i.Name()))
if err := i.SyncConfig(cfg); err != nil && !errors.Is(err, os.ErrNotExist) {
s.logger.Fatal("Failed to sync interface configuration",
@@ -56,9 +56,9 @@ func (s *Syncer) OnInterfaceAdded(i *core.Interface) {
}
}
func (s *Syncer) OnInterfaceRemoved(i *core.Interface) {}
func (s *ConfigSynchronization) OnInterfaceRemoved(i *core.Interface) {}
func (s *Syncer) watch() {
func (s *ConfigSynchronization) watch() {
watcher, err := fsnotify.NewWatcher()
if err != nil {
s.logger.Fatal("failed to create fsnotify watcher", zap.Error(err))
@@ -88,7 +88,7 @@ func (s *Syncer) watch() {
}
}
func (s *Syncer) handleFsnotifyEvent(event fsnotify.Event) {
func (s *ConfigSynchronization) handleFsnotifyEvent(event fsnotify.Event) {
cfg := event.Name
filename := path.Base(cfg)
extension := path.Ext(filename)

View File

@@ -14,7 +14,7 @@ import (
type gwHashV4 [2]byte
type gwHashV6 [8]byte
type Syncer struct {
type RouteSynchronization struct {
watcher *watcher.Watcher
gwMapV4 map[gwHashV4]*core.Peer
@@ -23,26 +23,26 @@ type Syncer struct {
logger *zap.Logger
}
func New(w *watcher.Watcher, table string) (*Syncer, error) {
s := &Syncer{
func New(w *watcher.Watcher, table string) (*RouteSynchronization, error) {
s := &RouteSynchronization{
watcher: w,
gwMapV4: map[gwHashV4]*core.Peer{},
gwMapV6: map[gwHashV6]*core.Peer{},
logger: zap.L().Named("sync.routes"),
}
w.OnInterface.Register(s)
w.OnInterface(s)
go s.watchKernel()
return s, nil
}
func (s *Syncer) OnInterfaceAdded(i *core.Interface) {
i.OnPeer.Register(s)
func (s *RouteSynchronization) OnInterfaceAdded(i *core.Interface) {
i.OnPeer(s)
}
func (s *Syncer) OnPeerAdded(p *core.Peer) {
func (s *RouteSynchronization) OnPeerAdded(p *core.Peer) {
ipV4 := p.PublicKey().IPv4Address()
hashV4 := *(*gwHashV4)(ipV4.IP[14:])
@@ -54,10 +54,10 @@ func (s *Syncer) OnPeerAdded(p *core.Peer) {
s.syncKernel() // Initial sync
p.OnModified.Register(s)
p.OnModified(s)
}
func (s *Syncer) OnPeerRemoved(p *core.Peer) {
func (s *RouteSynchronization) OnPeerRemoved(p *core.Peer) {
ipV4 := p.PublicKey().IPv4Address()
hashV4 := *(*gwHashV4)(ipV4.IP[14:])
@@ -68,7 +68,7 @@ func (s *Syncer) OnPeerRemoved(p *core.Peer) {
delete(s.gwMapV6, hashV6)
}
func (s *Syncer) OnPeerModified(p *core.Peer, old *wgtypes.Peer, m core.PeerModifier, ipsAdded, ipsRemoved []net.IPNet) {
func (s *RouteSynchronization) OnPeerModified(p *core.Peer, old *wgtypes.Peer, m core.PeerModifier, ipsAdded, ipsRemoved []net.IPNet) {
for _, dst := range ipsAdded {
if err := p.Interface.KernelDevice.AddRoute(&dst); err != nil {
s.logger.Error("Failed to add route", zap.Error(err))
@@ -94,4 +94,4 @@ func (s *Syncer) OnPeerModified(p *core.Peer, old *wgtypes.Peer, m core.PeerModi
}
}
func (s *Syncer) OnInterfaceRemoved(i *core.Interface) {}
func (s *RouteSynchronization) OnInterfaceRemoved(i *core.Interface) {}

View File

@@ -7,7 +7,7 @@ import (
"riasc.eu/wice/pkg/device"
)
func (s *Syncer) syncKernel() {
func (s *RouteSynchronization) syncKernel() {
routes, err := netlink.RouteList(nil, unix.AF_INET6)
if err != nil {
s.logger.Error("Failed to get routes from kernel", zap.Error(err))
@@ -21,7 +21,7 @@ func (s *Syncer) syncKernel() {
}
}
func (s *Syncer) watchKernel() {
func (s *RouteSynchronization) watchKernel() {
rus := make(chan netlink.RouteUpdate)
errs := make(chan error)
@@ -45,7 +45,7 @@ func (s *Syncer) watchKernel() {
}
}
func (s *Syncer) handleRouteUpdate(ru *netlink.RouteUpdate) {
func (s *RouteSynchronization) handleRouteUpdate(ru *netlink.RouteUpdate) {
s.logger.Debug("Received netlink route update", zap.Any("update", ru))
if ru.Protocol == device.RouteProtocol {

View File

@@ -7,7 +7,7 @@ import (
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"riasc.eu/wice/internal/wg"
"riasc.eu/wice/pkg/core"
icex "riasc.eu/wice/pkg/feat/disc/ice"
"riasc.eu/wice/pkg/feat/disc/ep"
"riasc.eu/wice/pkg/pb"
"riasc.eu/wice/pkg/signaling"
)
@@ -68,7 +68,7 @@ func (s *Server) OnPeerModified(p *core.Peer, old *wgtypes.Peer, mod core.PeerMo
}
}
func (s *Server) OnConnectionStateChange(p *icex.Peer, cs ice.ConnectionState) {
func (s *Server) OnConnectionStateChange(p *ep.Peer, cs ice.ConnectionState) {
s.events.C <- &pb.Event{
Type: pb.Event_PEER_CONNECTION_STATE_CHANGED,