mirror of https://github.com/status-im/consul.git
Merge pull request #1851 from hashicorp/f-ipv6-bind
Allow [::] as a bind address (binds to first public IPv6 address)
This commit is contained in:
commit
b6cd4318d6
|
@ -142,10 +142,16 @@ func Create(config *Config, logOutput io.Writer) (*Agent, error) {
|
||||||
if ip := net.ParseIP(config.AdvertiseAddr); ip == nil {
|
if ip := net.ParseIP(config.AdvertiseAddr); ip == nil {
|
||||||
return nil, fmt.Errorf("Failed to parse advertise address: %v", config.AdvertiseAddr)
|
return nil, fmt.Errorf("Failed to parse advertise address: %v", config.AdvertiseAddr)
|
||||||
}
|
}
|
||||||
} else if config.BindAddr != "0.0.0.0" && config.BindAddr != "" {
|
} else if config.BindAddr != "0.0.0.0" && config.BindAddr != "" && config.BindAddr != "[::]" {
|
||||||
config.AdvertiseAddr = config.BindAddr
|
config.AdvertiseAddr = config.BindAddr
|
||||||
} else {
|
} else {
|
||||||
ip, err := consul.GetPrivateIP()
|
var err error
|
||||||
|
var ip net.IP
|
||||||
|
if config.BindAddr == "[::]" {
|
||||||
|
ip, err = consul.GetPublicIPv6()
|
||||||
|
} else {
|
||||||
|
ip, err = consul.GetPrivateIP()
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Failed to get advertise address: %v", err)
|
return nil, fmt.Errorf("Failed to get advertise address: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -264,6 +264,56 @@ func getPrivateIP(addresses []net.Addr) (net.IP, error) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPublicIPv6 is used to return the first public IP address
|
||||||
|
// associated with an interface on the machine
|
||||||
|
func GetPublicIPv6() (net.IP, error) {
|
||||||
|
addresses, err := net.InterfaceAddrs()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to get interface addresses: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return getPublicIPv6(addresses)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isUniqueLocalAddress(ip net.IP) bool {
|
||||||
|
return len(ip) == net.IPv6len && ip[0] == 0xfc && ip[1] == 0x00
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPublicIPv6(addresses []net.Addr) (net.IP, error) {
|
||||||
|
var candidates []net.IP
|
||||||
|
|
||||||
|
// Find public IPv6 address
|
||||||
|
for _, rawAddr := range addresses {
|
||||||
|
var ip net.IP
|
||||||
|
switch addr := rawAddr.(type) {
|
||||||
|
case *net.IPAddr:
|
||||||
|
ip = addr.IP
|
||||||
|
case *net.IPNet:
|
||||||
|
ip = addr.IP
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip.To4() != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip.IsLinkLocalUnicast() || isUniqueLocalAddress(ip) || ip.IsLoopback() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
candidates = append(candidates, ip)
|
||||||
|
}
|
||||||
|
numIps := len(candidates)
|
||||||
|
switch numIps {
|
||||||
|
case 0:
|
||||||
|
return nil, fmt.Errorf("No public IPv6 address found")
|
||||||
|
case 1:
|
||||||
|
return candidates[0], nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("Multiple public IPv6 addresses found. Please configure one.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Converts bytes to an integer
|
// Converts bytes to an integer
|
||||||
func bytesToUint64(b []byte) uint64 {
|
func bytesToUint64(b []byte) uint64 {
|
||||||
return binary.BigEndian.Uint64(b)
|
return binary.BigEndian.Uint64(b)
|
||||||
|
|
|
@ -275,3 +275,96 @@ func TestGenerateUUID(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetPublicIPv6(t *testing.T) {
|
||||||
|
ip, _, err := net.ParseCIDR("fe80::1/128")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to parse link-local cidr: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ip2, _, err := net.ParseCIDR("::1/128")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to parse loopback cidr: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ip3, _, err := net.ParseCIDR("fc00::1/128")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to parse ULA cidr: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pubIP, _, err := net.ParseCIDR("2001:0db8:85a3::8a2e:0370:7334/128")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to parse public cidr: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
addrs []net.Addr
|
||||||
|
expected net.IP
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
addrs: []net.Addr{
|
||||||
|
&net.IPAddr{
|
||||||
|
IP: ip,
|
||||||
|
},
|
||||||
|
&net.IPAddr{
|
||||||
|
IP: ip2,
|
||||||
|
},
|
||||||
|
&net.IPAddr{
|
||||||
|
IP: ip3,
|
||||||
|
},
|
||||||
|
&net.IPAddr{
|
||||||
|
IP: pubIP,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: pubIP,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
addrs: []net.Addr{
|
||||||
|
&net.IPAddr{
|
||||||
|
IP: ip,
|
||||||
|
},
|
||||||
|
&net.IPAddr{
|
||||||
|
IP: ip2,
|
||||||
|
},
|
||||||
|
&net.IPAddr{
|
||||||
|
IP: ip3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: errors.New("No public IPv6 address found"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
addrs: []net.Addr{
|
||||||
|
&net.IPAddr{
|
||||||
|
IP: ip,
|
||||||
|
},
|
||||||
|
&net.IPAddr{
|
||||||
|
IP: ip,
|
||||||
|
},
|
||||||
|
&net.IPAddr{
|
||||||
|
IP: pubIP,
|
||||||
|
},
|
||||||
|
&net.IPAddr{
|
||||||
|
IP: pubIP,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: errors.New("Multiple public IPv6 addresses found. Please configure one."),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
ip, err := getPublicIPv6(test.addrs)
|
||||||
|
switch {
|
||||||
|
case test.err != nil && err != nil:
|
||||||
|
if err.Error() != test.err.Error() {
|
||||||
|
t.Fatalf("unexpected error: %v != %v", test.err, err)
|
||||||
|
}
|
||||||
|
case (test.err == nil && err != nil) || (test.err != nil && err == nil):
|
||||||
|
t.Fatalf("unexpected error: %v != %v", test.err, err)
|
||||||
|
default:
|
||||||
|
if !test.expected.Equal(ip) {
|
||||||
|
t.Fatalf("unexpected ip: %v != %v", ip, test.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -93,7 +93,8 @@ The options below are all specified on the command-line.
|
||||||
for internal cluster communications.
|
for internal cluster communications.
|
||||||
This is an IP address that should be reachable by all other nodes in the cluster.
|
This is an IP address that should be reachable by all other nodes in the cluster.
|
||||||
By default, this is "0.0.0.0", meaning Consul will use the first available private
|
By default, this is "0.0.0.0", meaning Consul will use the first available private
|
||||||
IP address. Consul uses both TCP and UDP and the same port for both. If you
|
IPv4 address. If you specify "[::]", Consul will use the first available public IPv6 address.
|
||||||
|
Consul uses both TCP and UDP and the same port for both. If you
|
||||||
have any firewalls, be sure to allow both protocols.
|
have any firewalls, be sure to allow both protocols.
|
||||||
|
|
||||||
* <a name="_client"></a><a href="#_client">`-client`</a> - The address to which
|
* <a name="_client"></a><a href="#_client">`-client`</a> - The address to which
|
||||||
|
|
Loading…
Reference in New Issue