2019-03-25 01:02:10 +01:00
package main
import (
"fmt"
"net"
2020-05-07 10:25:58 +02:00
"github.com/costela/wesher/cluster"
2019-03-27 00:26:59 +01:00
"github.com/hashicorp/go-sockaddr"
2020-05-10 18:23:46 +02:00
"github.com/pkg/errors"
2019-03-25 01:02:10 +01:00
"github.com/stevenroose/gonfig"
)
type config struct {
2019-03-25 23:31:32 +01:00
ClusterKey [ ] byte ` id:"cluster-key" desc:"shared key for cluster membership; must be 32 bytes base64 encoded; will be generated if not provided" `
Join [ ] string ` desc:"comma separated list of hostnames or IP addresses to existing cluster members; if not provided, will attempt resuming any known state or otherwise wait for further members." `
2019-03-27 22:23:15 +01:00
Init bool ` desc:"whether to explicitly (re)initialize the cluster; any known state from previous runs will be forgotten" `
2020-05-14 12:01:48 +02:00
Banner string ` id:"banner" desc:"a string appended to /etc/hosts banner to discriminate between multiple running clusters" `
StatePath string ` id:"state-path" desc:"path where the cluster state is stored; optional, depends on cluster name if not set" `
2019-07-21 23:00:18 +02:00
BindAddr string ` id:"bind-addr" desc:"IP address to bind to for cluster membership traffic (cannot be used with --bind-iface)" `
BindIface string ` id:"bind-iface" desc:"Interface to bind to for cluster membership traffic (cannot be used with --bind-addr)" `
2019-03-25 23:31:32 +01:00
ClusterPort int ` id:"cluster-port" desc:"port used for membership gossip traffic (both TCP and UDP); must be the same across cluster" default:"7946" `
WireguardPort int ` id:"wireguard-port" desc:"port used for wireguard traffic (UDP); must be the same across cluster" default:"51820" `
OverlayNet * network ` id:"overlay-net" desc:"the network in which to allocate addresses for the overlay mesh network (CIDR format); smaller networks increase the chance of IP collision" default:"10.0.0.0/8" `
Interface string ` desc:"name of the wireguard interface to create and manage" default:"wgoverlay" `
2019-03-26 23:26:29 +01:00
NoEtcHosts bool ` id:"no-etc-hosts" desc:"disable writing of entries to /etc/hosts" `
2019-03-25 23:31:32 +01:00
LogLevel string ` id:"log-level" desc:"set the verbosity (debug/info/warn/error)" default:"warn" `
2019-03-26 23:26:54 +01:00
Version bool ` desc:"display current version and exit" `
2019-03-25 01:02:10 +01:00
2019-03-26 23:26:54 +01:00
// for easier local testing; will break etchosts entry
UseIPAsName bool ` id:"ip-as-name" default:"false" opts:"hidden" `
2019-03-25 01:02:10 +01:00
}
func loadConfig ( ) ( * config , error ) {
var config config
err := gonfig . Load ( & config , gonfig . Conf { EnvPrefix : "WESHER_" } )
if err != nil {
return nil , err
}
// perform some validation
2020-05-07 10:25:58 +02:00
if len ( config . ClusterKey ) != 0 && len ( config . ClusterKey ) != cluster . KeyLen {
return nil , fmt . Errorf ( "unsupported cluster key length; expected %d, got %d" , cluster . KeyLen , len ( config . ClusterKey ) )
2019-03-25 01:02:10 +01:00
}
if bits , _ := ( ( * net . IPNet ) ( config . OverlayNet ) ) . Mask . Size ( ) ; bits % 8 != 0 {
return nil , fmt . Errorf ( "unsupported overlay network size; net mask must be multiple of 8, got %d" , bits )
}
2019-07-21 23:00:18 +02:00
if config . BindAddr != "" && config . BindIface != "" {
return nil , fmt . Errorf ( "setting both bind address and bind interface is not supported" )
2020-05-07 10:13:23 +02:00
} else if config . BindIface != "" {
// Compute the actual bind address based on the provided interface
iface , err := net . InterfaceByName ( config . BindIface )
if err != nil {
2020-05-10 18:23:46 +02:00
return nil , errors . Wrapf ( err , "could not get interface by name %s" , config . BindIface )
2020-05-07 10:13:23 +02:00
}
addrs , err := iface . Addrs ( )
if err != nil {
2020-05-10 18:23:46 +02:00
return nil , errors . Wrapf ( err , "could not get addresses for interface %s" , config . BindIface )
2020-05-07 10:13:23 +02:00
}
if len ( addrs ) > 0 {
if addr , ok := addrs [ 0 ] . ( * net . IPNet ) ; ok {
config . BindAddr = addr . IP . String ( )
}
}
2019-07-21 23:00:18 +02:00
} else if config . BindAddr == "" && config . BindIface == "" {
2020-05-07 10:13:23 +02:00
// FIXME: this is a workaround for memberlist refusing to listen on public IPs if BindAddr==0.0.0.0
2019-03-27 00:47:23 +01:00
detectedBindAddr , err := sockaddr . GetPublicIP ( )
2019-03-27 00:26:59 +01:00
if err != nil {
return nil , err
}
// if we cannot find a public IP, let memberlist do its thing
2019-03-27 00:47:23 +01:00
if detectedBindAddr != "" {
config . BindAddr = detectedBindAddr
2019-03-27 22:23:15 +01:00
} else {
config . BindAddr = "0.0.0.0"
2019-03-27 00:26:59 +01:00
}
}
2019-03-25 01:02:10 +01:00
return & config , nil
}
type network net . IPNet
// UnmarshalText parses the provided byte array into the network receiver
func ( n * network ) UnmarshalText ( data [ ] byte ) error {
_ , ipnet , err := net . ParseCIDR ( string ( data ) )
if err != nil {
return err
}
* n = network ( * ipnet )
return nil
}