2023-10-01 20:01:35 +02:00
|
|
|
// conf defines configuration file parsing for golang
|
|
|
|
package conf
|
|
|
|
|
|
|
|
import (
|
|
|
|
"os"
|
|
|
|
|
2023-12-10 20:21:54 +01:00
|
|
|
"github.com/go-playground/validator/v10"
|
2023-10-01 20:01:35 +02:00
|
|
|
"gopkg.in/yaml.v3"
|
|
|
|
)
|
|
|
|
|
2023-11-05 19:03:58 +01:00
|
|
|
type WgMeshConfigurationError struct {
|
|
|
|
msg string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *WgMeshConfigurationError) Error() string {
|
|
|
|
return m.msg
|
|
|
|
}
|
|
|
|
|
2023-11-21 17:42:49 +01:00
|
|
|
type NodeType string
|
|
|
|
|
|
|
|
const (
|
|
|
|
PEER_ROLE NodeType = "peer"
|
|
|
|
CLIENT_ROLE NodeType = "client"
|
|
|
|
)
|
|
|
|
|
2023-11-24 13:07:03 +01:00
|
|
|
type IPDiscovery string
|
|
|
|
|
|
|
|
const (
|
2023-12-22 22:47:56 +01:00
|
|
|
PUBLIC_IP_DISCOVERY IPDiscovery = "public"
|
|
|
|
DNS_IP_DISCOVERY IPDiscovery = "dns"
|
2023-11-24 13:07:03 +01:00
|
|
|
)
|
|
|
|
|
2023-12-10 20:21:54 +01:00
|
|
|
// WgConfiguration contains per-mesh WireGuard configuration. Contains poitner types only so we can
|
|
|
|
// tell if the attribute is set
|
|
|
|
type WgConfiguration struct {
|
|
|
|
// IPDIscovery: how to discover your IP if not specified. Use your outgoing IP or use a public
|
|
|
|
// service for IPDiscoverability
|
|
|
|
IPDiscovery *IPDiscovery `yaml:"ipDiscovery" validate:"required,eq=public|eq=dns"`
|
|
|
|
// AdvertiseRoutes: specifies whether the node can act as a router routing packets between meshes
|
2023-12-12 12:58:47 +01:00
|
|
|
AdvertiseRoutes *bool `yaml:"advertiseRoute" validate:"required"`
|
2023-12-10 20:21:54 +01:00
|
|
|
// AdvertiseDefaultRoute: specifies whether or not this route should advertise a default route
|
|
|
|
// for all nodes to route their packets to
|
2023-12-12 12:58:47 +01:00
|
|
|
AdvertiseDefaultRoute *bool `yaml:"advertiseDefaults" validate:"required"`
|
2023-12-10 20:21:54 +01:00
|
|
|
// Endpoint contains what value should be set as the public endpoint of this node
|
|
|
|
Endpoint *string `yaml:"publicEndpoint"`
|
|
|
|
// Role specifies whether or not the user is globally accessible.
|
|
|
|
// If the user is globaly accessible they specify themselves as a client.
|
|
|
|
Role *NodeType `yaml:"role" validate:"required,eq=client|eq=peer"`
|
|
|
|
// KeepAliveWg configures the implementation so that we send keep alive packets to peers.
|
|
|
|
// KeepAlive can only be set if role is type client
|
|
|
|
KeepAliveWg *int `yaml:"keepAliveWg" validate:"omitempty,gte=0"`
|
|
|
|
// PreUp are WireGuard commands to run before adding the WG interface
|
|
|
|
PreUp []string `yaml:"preUp"`
|
|
|
|
// PostUp are WireGuard commands to run after adding the WG interface
|
|
|
|
PostUp []string `yaml:"postUp"`
|
|
|
|
// PreDown are WireGuard commands to run prior to removing the WG interface
|
|
|
|
PreDown []string `yaml:"preDown"`
|
|
|
|
// PostDown are WireGuard command to run after removing the WG interface
|
|
|
|
PostDown []string `yaml:"postDown"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type DaemonConfiguration struct {
|
2023-10-28 17:38:25 +02:00
|
|
|
// CertificatePath is the path to the certificate to use in mTLS
|
2023-12-22 22:47:56 +01:00
|
|
|
CertificatePath string `yaml:"certificatePath" validate:"required"`
|
2023-10-28 17:38:25 +02:00
|
|
|
// PrivateKeypath is the path to the clients private key in mTLS
|
2023-12-22 22:47:56 +01:00
|
|
|
PrivateKeyPath string `yaml:"privateKeyPath" validate:"required"`
|
2023-10-28 17:38:25 +02:00
|
|
|
// CaCeritifcatePath path to the certificate of the trust certificate authority
|
2023-12-22 22:47:56 +01:00
|
|
|
CaCertificatePath string `yaml:"caCertificatePath" validate:"required"`
|
2023-10-28 17:38:25 +02:00
|
|
|
// SkipCertVerification specify to skip certificate verification. Should only be used
|
|
|
|
// in test environments
|
|
|
|
SkipCertVerification bool `yaml:"skipCertVerification"`
|
|
|
|
// Port to run the GrpcServer on
|
2023-12-10 20:21:54 +01:00
|
|
|
GrpcPort int `yaml:"gRPCPort" validate:"required"`
|
|
|
|
// Timeout number of seconds without response that a node is considered unreachable by gRPC
|
|
|
|
Timeout int `yaml:"timeout" validate:"required,gte=1"`
|
2023-11-20 12:28:12 +01:00
|
|
|
// Profile whether or not to include a http server that profiles the code
|
|
|
|
Profile bool `yaml:"profile"`
|
|
|
|
// StubWg whether or not to stub the WireGuard types
|
|
|
|
StubWg bool `yaml:"stubWg"`
|
2023-12-10 20:21:54 +01:00
|
|
|
// SyncRate specifies how long the minimum time should be between synchronisation
|
|
|
|
SyncRate int `yaml:"syncRate" validate:"required,gte=1"`
|
|
|
|
// KeepAliveTime: number of seconds before the leader of the mesh sends an update to
|
|
|
|
// send to every member in the mesh
|
|
|
|
KeepAliveTime int `yaml:"keepAliveTime" validate:"required,gte=1"`
|
|
|
|
// ClusterSize specifies how many neighbours you should synchronise with per round
|
2023-12-22 22:47:56 +01:00
|
|
|
ClusterSize int `yaml:"clusterSize" validate:"gte=1"`
|
2023-12-10 20:21:54 +01:00
|
|
|
// InterClusterChance specifies the probabilityof inter-cluster communication in a sync round
|
2023-12-22 22:47:56 +01:00
|
|
|
InterClusterChance float64 `yaml:"interClusterChance" validate:"gt=0"`
|
2023-12-10 20:21:54 +01:00
|
|
|
// BranchRate specifies the number of nodes to synchronise with when a node has
|
|
|
|
// new changes to send to the mesh
|
|
|
|
BranchRate int `yaml:"branchRate" validate:"required,gte=1"`
|
|
|
|
// InfectionCount: number of time to sync before an update can no longer be 'caught'
|
|
|
|
InfectionCount int `yaml:"infectionCount" validate:"required,gte=1"`
|
|
|
|
// BaseConfiguration base WireGuard configuration to use, this is used when none is provided
|
|
|
|
BaseConfiguration WgConfiguration `yaml:"baseConfiguration" validate:"required"`
|
2023-10-01 20:01:35 +02:00
|
|
|
}
|
|
|
|
|
2023-12-10 20:21:54 +01:00
|
|
|
// ValdiateMeshConfiguration: validates the mesh configuration
|
|
|
|
func ValidateMeshConfiguration(conf *WgConfiguration) error {
|
|
|
|
validate := validator.New(validator.WithRequiredStructEnabled())
|
|
|
|
err := validate.Struct(conf)
|
2023-11-05 19:03:58 +01:00
|
|
|
|
2023-12-10 20:21:54 +01:00
|
|
|
if conf.PostDown == nil {
|
|
|
|
conf.PostDown = make([]string, 0)
|
2023-11-05 19:03:58 +01:00
|
|
|
}
|
|
|
|
|
2023-12-10 20:21:54 +01:00
|
|
|
if conf.PostUp == nil {
|
|
|
|
conf.PostUp = make([]string, 0)
|
2023-11-05 19:03:58 +01:00
|
|
|
}
|
|
|
|
|
2023-12-10 20:21:54 +01:00
|
|
|
if conf.PreDown == nil {
|
|
|
|
conf.PreDown = make([]string, 0)
|
2023-11-05 19:03:58 +01:00
|
|
|
}
|
|
|
|
|
2023-12-10 20:21:54 +01:00
|
|
|
if conf.PreUp == nil {
|
|
|
|
conf.PreUp = make([]string, 0)
|
2023-11-05 19:03:58 +01:00
|
|
|
}
|
|
|
|
|
2023-12-10 20:21:54 +01:00
|
|
|
return err
|
|
|
|
}
|
2023-11-05 19:03:58 +01:00
|
|
|
|
2023-12-10 20:21:54 +01:00
|
|
|
// ValidateDaemonConfiguration: validates the dameon configuration that is used.
|
|
|
|
func ValidateDaemonConfiguration(c *DaemonConfiguration) error {
|
|
|
|
validate := validator.New(validator.WithRequiredStructEnabled())
|
|
|
|
err := validate.Struct(c)
|
|
|
|
return err
|
|
|
|
}
|
2023-11-06 14:37:28 +01:00
|
|
|
|
2023-12-10 20:21:54 +01:00
|
|
|
// ParseMeshConfiguration: parses the mesh network configuration. Parses parameters such as
|
|
|
|
// keepalive time, role and so forth.
|
|
|
|
func ParseMeshConfiguration(filePath string) (*WgConfiguration, error) {
|
|
|
|
var conf WgConfiguration
|
2023-11-24 13:37:54 +01:00
|
|
|
|
2023-12-10 20:21:54 +01:00
|
|
|
yamlBytes, err := os.ReadFile(filePath)
|
2023-11-06 14:37:28 +01:00
|
|
|
|
2023-12-10 20:21:54 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2023-11-06 10:54:06 +01:00
|
|
|
}
|
|
|
|
|
2023-12-10 20:21:54 +01:00
|
|
|
err = yaml.Unmarshal(yamlBytes, &conf)
|
2023-11-21 17:42:49 +01:00
|
|
|
|
2023-12-10 20:21:54 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2023-11-24 13:07:03 +01:00
|
|
|
}
|
|
|
|
|
2023-12-10 20:21:54 +01:00
|
|
|
return &conf, ValidateMeshConfiguration(&conf)
|
2023-11-05 19:03:58 +01:00
|
|
|
}
|
|
|
|
|
2023-12-10 20:21:54 +01:00
|
|
|
// ParseDaemonConfiguration parses the mesh configuration and validates the configuration
|
|
|
|
func ParseDaemonConfiguration(filePath string) (*DaemonConfiguration, error) {
|
|
|
|
var conf DaemonConfiguration
|
2023-10-01 20:01:35 +02:00
|
|
|
|
|
|
|
yamlBytes, err := os.ReadFile(filePath)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = yaml.Unmarshal(yamlBytes, &conf)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-12-10 20:21:54 +01:00
|
|
|
return &conf, ValidateDaemonConfiguration(&conf)
|
|
|
|
}
|
|
|
|
|
|
|
|
// MergemeshConfiguration: merges the configuration in precedence where the last
|
|
|
|
// element in the list takes the most and the first takes the least
|
|
|
|
func MergeMeshConfiguration(cfgs ...WgConfiguration) (WgConfiguration, error) {
|
|
|
|
var result WgConfiguration
|
|
|
|
|
|
|
|
for _, cfg := range cfgs {
|
|
|
|
if cfg.AdvertiseDefaultRoute != nil {
|
|
|
|
result.AdvertiseDefaultRoute = cfg.AdvertiseDefaultRoute
|
|
|
|
}
|
|
|
|
|
|
|
|
if cfg.AdvertiseRoutes != nil {
|
|
|
|
result.AdvertiseRoutes = cfg.AdvertiseRoutes
|
|
|
|
}
|
|
|
|
|
|
|
|
if cfg.Endpoint != nil {
|
|
|
|
result.Endpoint = cfg.Endpoint
|
|
|
|
}
|
|
|
|
|
|
|
|
if cfg.IPDiscovery != nil {
|
|
|
|
result.IPDiscovery = cfg.IPDiscovery
|
|
|
|
}
|
|
|
|
|
|
|
|
if cfg.KeepAliveWg != nil {
|
|
|
|
result.KeepAliveWg = cfg.KeepAliveWg
|
|
|
|
}
|
|
|
|
|
|
|
|
if cfg.PostDown != nil {
|
|
|
|
result.PostDown = cfg.PostDown
|
|
|
|
}
|
|
|
|
|
|
|
|
if cfg.PostUp != nil {
|
|
|
|
result.PostUp = cfg.PostUp
|
|
|
|
}
|
|
|
|
|
|
|
|
if cfg.PreDown != nil {
|
|
|
|
result.PreDown = cfg.PreDown
|
|
|
|
}
|
|
|
|
|
|
|
|
if cfg.PreUp != nil {
|
|
|
|
result.PreUp = cfg.PreUp
|
|
|
|
}
|
|
|
|
|
|
|
|
if cfg.Role != nil {
|
|
|
|
result.Role = cfg.Role
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, ValidateMeshConfiguration(&result)
|
2023-10-01 20:01:35 +02:00
|
|
|
}
|