diff --git a/README.md b/README.md index f57c70f..9c995fc 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,8 @@ All options can be passed either as command-line flags or environment variables: | `--cluster-key KEY` | WESHER_CLUSTER_KEY | shared key for cluster membership; must be 32 bytes base64 encoded; will be generated if not provided | autogenerated/loaded | | `--join HOST,...` | WESHER_JOIN | 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 | | | `--init` | WESHER_INIT | whether to explicitly (re)initialize the cluster; any known state from previous runs will be forgotten | `false` | -| `--bind-addr ADDR` | WESHER_BIND_ADDR | IP address to bind to for cluster membership | autodetected | +| `--bind-addr ADDR` | WESHER_BIND_ADDR | IP address to bind to for cluster membership (cannot be used with --bind-iface) | autodetected | +| `--bind-iface IFACE` | WESHER_BIND_IFACE | Interface to bind to for cluster membership (cannot be used with --bind-addr)| | | `--cluster-port PORT` | WESHER_CLUSTER_PORT | port used for membership gossip traffic (both TCP and UDP); must be the same across cluster | `7946` | | `--wireguard-port PORT` | WESHER_WIREGUARD_PORT | port used for wireguard traffic (UDP); must be the same across cluster | `51820` | | `--overlay-net ADDR/MASK` | WESHER_OVERLAY_NET | the network in which to allocate addresses for the overlay mesh network (CIDR format); smaller networks increase the chance of IP collision | `10.0.0.0/8` | diff --git a/cluster.go b/cluster.go index bfad099..2c0c0da 100644 --- a/cluster.go +++ b/cluster.go @@ -64,10 +64,28 @@ func newCluster(config *config, wg *wgState) (*cluster, error) { } state.ClusterKey = clusterKey + // we check for mutual exclusion in config.go + bindAddr := config.BindAddr + if config.BindIface != "" { + iface, err := net.InterfaceByName(config.BindIface) + if err != nil { + return nil, err + } + addrs, err := iface.Addrs() + if err != nil { + return nil, err + } + if len(addrs) > 0 { + if addr, ok := addrs[0].(*net.IPNet); ok { + bindAddr = addr.IP.String() + } + } + } + mlConfig := memberlist.DefaultWANConfig() mlConfig.LogOutput = logrus.StandardLogger().WriterLevel(logrus.DebugLevel) mlConfig.SecretKey = clusterKey - mlConfig.BindAddr = config.BindAddr + mlConfig.BindAddr = bindAddr mlConfig.BindPort = config.ClusterPort mlConfig.AdvertisePort = config.ClusterPort if config.UseIPAsName && config.BindAddr != "0.0.0.0" { diff --git a/config.go b/config.go index b13130e..e1a1c93 100644 --- a/config.go +++ b/config.go @@ -14,7 +14,8 @@ type config struct { 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."` Init bool `desc:"whether to explicitly (re)initialize the cluster; any known state from previous runs will be forgotten"` - BindAddr string `id:"bind-addr" desc:"IP address to bind to for cluster membership"` + 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)"` 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"` @@ -44,7 +45,9 @@ func loadConfig() (*config, error) { } // FIXME: this is a workaround for memberlist refusing to listen on public IPs if BindAddr==0.0.0.0 - if config.BindAddr == "" { + if config.BindAddr != "" && config.BindIface != "" { + return nil, fmt.Errorf("setting both bind address and bind interface is not supported") + } else if config.BindAddr == "" && config.BindIface == "" { detectedBindAddr, err := sockaddr.GetPublicIP() if err != nil { return nil, err