Rather than pick addresses totally at random, this changes the preference to be private IP4 > public IP4 > DNS. (Ordering is not guaranteed, but in many cases there will only be one of each type of IP anyways.) All three of these are preferred over IP6, which also goes public > private. This seems likely to match the common case where you're getting into a machine on the private side from the public side. It's harder to know if DNS is private or not, so IPs are preferred in this case.
118 lines
3.3 KiB
Go
118 lines
3.3 KiB
Go
package endpoint
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
|
|
"github.com/hashicorp/boundary/internal/errors"
|
|
)
|
|
|
|
type preferencer struct {
|
|
matchers []matcher
|
|
}
|
|
|
|
// NewPreferencer builds up a preferencer with a set of preference options. This
|
|
// can then be used with Choose to select preferences from among a set of IP
|
|
// addresses and DNS names.
|
|
//
|
|
// Supported options: WithPreferenceOrder
|
|
func NewPreferencer(ctx context.Context, opt ...Option) (*preferencer, error) {
|
|
const op = "endpoint.NewPreferencer"
|
|
opts, err := getOpts(opt...)
|
|
if err != nil {
|
|
return nil, errors.Wrap(ctx, err, op, errors.WithCode(errors.InvalidParameter))
|
|
}
|
|
pref := &preferencer{
|
|
matchers: opts.withMatchers,
|
|
}
|
|
return pref, nil
|
|
}
|
|
|
|
// Choose takes in IP addresses and/or DNS names and chooses an endpoint from
|
|
// among them, picking one at random if there are no preferences supplied. If
|
|
// preferences are specified but none match, the empty string is returned.
|
|
// However, if no IP addresses or DNS names are supplied, an error is returned.
|
|
//
|
|
// Supported options: WithIpAddrs, WithDnsNames
|
|
func (p *preferencer) Choose(ctx context.Context, opt ...Option) (string, error) {
|
|
const op = "endpoint.(preferencer).Choose"
|
|
opts, err := getOpts(opt...)
|
|
if err != nil {
|
|
return "", errors.Wrap(ctx, err, op)
|
|
}
|
|
if len(opts.withIpAddrs)+len(opts.withDnsNames) == 0 {
|
|
return "", errors.New(ctx, errors.InvalidParameter, op, "no ip addresses or dns names passed in")
|
|
}
|
|
|
|
switch len(p.matchers) {
|
|
case 0:
|
|
// We have no matchers, so pick a private address if we have one (since
|
|
// those are most likely to be what the user is trying to gain access to
|
|
// via a worker). If we don't have one, pick any IP address, as that's
|
|
// also more likely desired than a DNS name. Otherwise, pick a DNS name.
|
|
// IPv6 follows same rules as 4, but least preferenced.
|
|
nonPrivateIp4s := make([]string, 0, len(opts.withIpAddrs))
|
|
nonPrivateIp6s := make([]string, 0, len(opts.withIpAddrs))
|
|
privateIp6s := make([]string, 0, len(opts.withIpAddrs))
|
|
|
|
for _, ipStr := range opts.withIpAddrs {
|
|
ipVal := net.ParseIP(ipStr)
|
|
if ipVal != nil {
|
|
switch ipVal.To4() {
|
|
case nil: // it's v6
|
|
if ipVal.IsPrivate() {
|
|
privateIp6s = append(privateIp6s, ipStr)
|
|
} else {
|
|
nonPrivateIp6s = append(nonPrivateIp6s, ipStr)
|
|
}
|
|
default:
|
|
if ipVal.IsPrivate() {
|
|
// private IPv4 is most highly preferenced, so return it directly
|
|
return ipStr, nil
|
|
}
|
|
nonPrivateIp4s = append(nonPrivateIp4s, ipStr)
|
|
}
|
|
}
|
|
}
|
|
|
|
switch {
|
|
case len(nonPrivateIp4s) > 0:
|
|
return nonPrivateIp4s[0], nil
|
|
|
|
case len(opts.withDnsNames) > 0:
|
|
return opts.withDnsNames[0], nil
|
|
|
|
case len(privateIp6s) > 0:
|
|
return privateIp6s[0], nil
|
|
|
|
case len(nonPrivateIp6s) > 0:
|
|
return nonPrivateIp6s[0], nil
|
|
|
|
default:
|
|
// We literally have nothing if we get here, so return nothing
|
|
return "", nil
|
|
}
|
|
|
|
default:
|
|
for _, m := range p.matchers {
|
|
switch m.(type) {
|
|
case dnsMatcher:
|
|
for _, name := range opts.withDnsNames {
|
|
if m.Match(name) {
|
|
return name, nil
|
|
}
|
|
}
|
|
case cidrMatcher:
|
|
for _, addr := range opts.withIpAddrs {
|
|
if m.Match(addr) {
|
|
return addr, nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Nothing matched. Don't treat it as an error, let the calling function
|
|
// simply ignore the empty result.
|
|
return "", nil
|
|
}
|
|
}
|