daemon: move most of validateEndpointSettings into api/t/net
Signed-off-by: Albin Kerouanton <albinker@gmail.com>
This commit is contained in:
parent
81ab8db1c3
commit
3092b261e2
3 changed files with 129 additions and 53 deletions
|
@ -1,5 +1,13 @@
|
|||
package network
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/docker/docker/internal/multierror"
|
||||
)
|
||||
|
||||
// EndpointSettings stores the network endpoint details
|
||||
type EndpointSettings struct {
|
||||
// Configurations
|
||||
|
@ -52,3 +60,76 @@ func (cfg *EndpointIPAMConfig) Copy() *EndpointIPAMConfig {
|
|||
cfgCopy.LinkLocalIPs = append(cfgCopy.LinkLocalIPs, cfg.LinkLocalIPs...)
|
||||
return &cfgCopy
|
||||
}
|
||||
|
||||
// NetworkSubnet describes a user-defined subnet for a specific network. It's only used to validate if an
|
||||
// EndpointIPAMConfig is valid for a specific network.
|
||||
type NetworkSubnet interface {
|
||||
// Contains checks whether the NetworkSubnet contains [addr].
|
||||
Contains(addr net.IP) bool
|
||||
// IsStatic checks whether the subnet was statically allocated (ie. user-defined).
|
||||
IsStatic() bool
|
||||
}
|
||||
|
||||
// IsInRange checks whether static IP addresses are valid in a specific network.
|
||||
func (cfg *EndpointIPAMConfig) IsInRange(v4Subnets []NetworkSubnet, v6Subnets []NetworkSubnet) error {
|
||||
var errs []error
|
||||
|
||||
if err := validateEndpointIPAddress(cfg.IPv4Address, v4Subnets); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if err := validateEndpointIPAddress(cfg.IPv6Address, v6Subnets); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
return multierror.Join(errs...)
|
||||
}
|
||||
|
||||
func validateEndpointIPAddress(epAddr string, ipamSubnets []NetworkSubnet) error {
|
||||
if epAddr == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var staticSubnet bool
|
||||
parsedAddr := net.ParseIP(epAddr)
|
||||
for _, subnet := range ipamSubnets {
|
||||
if subnet.IsStatic() {
|
||||
staticSubnet = true
|
||||
if subnet.Contains(parsedAddr) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if staticSubnet {
|
||||
return fmt.Errorf("no configured subnet or ip-range contain the IP address %s", epAddr)
|
||||
}
|
||||
|
||||
return errors.New("user specified IP address is supported only when connecting to networks with user configured subnets")
|
||||
}
|
||||
|
||||
// Validate checks whether cfg is valid.
|
||||
func (cfg *EndpointIPAMConfig) Validate() error {
|
||||
if cfg == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var errs []error
|
||||
|
||||
if cfg.IPv4Address != "" {
|
||||
if addr := net.ParseIP(cfg.IPv4Address); addr == nil || addr.To4() == nil || addr.IsUnspecified() {
|
||||
errs = append(errs, fmt.Errorf("invalid IPv4 address: %s", cfg.IPv4Address))
|
||||
}
|
||||
}
|
||||
if cfg.IPv6Address != "" {
|
||||
if addr := net.ParseIP(cfg.IPv6Address); addr == nil || addr.To4() != nil || addr.IsUnspecified() {
|
||||
errs = append(errs, fmt.Errorf("invalid IPv6 address: %s", cfg.IPv6Address))
|
||||
}
|
||||
}
|
||||
for _, addr := range cfg.LinkLocalIPs {
|
||||
if parsed := net.ParseIP(addr); parsed == nil || parsed.IsUnspecified() {
|
||||
errs = append(errs, fmt.Errorf("invalid link-local IP address: %s", addr))
|
||||
}
|
||||
}
|
||||
|
||||
return multierror.Join(errs...)
|
||||
}
|
||||
|
|
|
@ -563,9 +563,8 @@ func (daemon *Daemon) allocateNetwork(cfg *config.Config, container *container.C
|
|||
return nil
|
||||
}
|
||||
|
||||
// validateEndpointSettings checks whether the given epConfig is valid. The nw parameter might be nil as a container
|
||||
// can be created with a reference to a network that don't exist yet. In that case, only partial validation will be
|
||||
// done.
|
||||
// validateEndpointSettings checks whether the given epConfig is valid. The nw parameter can be nil, in which case it
|
||||
// won't try to check if the endpoint IP addresses are within network's subnets.
|
||||
func validateEndpointSettings(nw *libnetwork.Network, nwName string, epConfig *networktypes.EndpointSettings) error {
|
||||
if epConfig == nil {
|
||||
return nil
|
||||
|
@ -578,6 +577,8 @@ func validateEndpointSettings(nw *libnetwork.Network, nwName string, epConfig *n
|
|||
|
||||
var errs []error
|
||||
|
||||
// TODO(aker): move this into api/types/network/endpoint.go once enableIPOnPredefinedNetwork and
|
||||
// serviceDiscoveryOnDefaultNetwork are removed.
|
||||
if !containertypes.NetworkMode(nwName).IsUserDefined() {
|
||||
hasStaticAddresses := ipamConfig.IPv4Address != "" || ipamConfig.IPv6Address != ""
|
||||
// On Linux, user specified IP address is accepted only by networks with user specified subnets.
|
||||
|
@ -589,63 +590,33 @@ func validateEndpointSettings(nw *libnetwork.Network, nwName string, epConfig *n
|
|||
}
|
||||
}
|
||||
|
||||
if ipamConfig.IPv4Address != "" {
|
||||
if addr := net.ParseIP(ipamConfig.IPv4Address); addr == nil || addr.To4() == nil || addr.IsUnspecified() {
|
||||
errs = append(errs, fmt.Errorf("invalid IPv4 address: %s", ipamConfig.IPv4Address))
|
||||
}
|
||||
// TODO(aker): add a proper multierror.Append
|
||||
if err := ipamConfig.Validate(); err != nil {
|
||||
errs = append(errs, err.(interface{ Unwrap() []error }).Unwrap()...)
|
||||
}
|
||||
if ipamConfig.IPv6Address != "" {
|
||||
if addr := net.ParseIP(ipamConfig.IPv6Address); addr == nil || addr.To4() != nil || addr.IsUnspecified() {
|
||||
errs = append(errs, fmt.Errorf("invalid IPv6 address: %s", ipamConfig.IPv6Address))
|
||||
|
||||
if nw != nil {
|
||||
_, _, v4Configs, v6Configs := nw.IpamConfig()
|
||||
|
||||
var nwIPv4Subnets, nwIPv6Subnets []networktypes.NetworkSubnet
|
||||
for _, nwIPAMConfig := range v4Configs {
|
||||
nwIPv4Subnets = append(nwIPv4Subnets, nwIPAMConfig)
|
||||
}
|
||||
}
|
||||
for _, addr := range ipamConfig.LinkLocalIPs {
|
||||
if parsed := net.ParseIP(addr); parsed == nil || parsed.IsUnspecified() {
|
||||
errs = append(errs, fmt.Errorf("invalid link-local IP address %s", addr))
|
||||
for _, nwIPAMConfig := range v6Configs {
|
||||
nwIPv6Subnets = append(nwIPv6Subnets, nwIPAMConfig)
|
||||
}
|
||||
|
||||
// TODO(aker): add a proper multierror.Append
|
||||
if err := ipamConfig.IsInRange(nwIPv4Subnets, nwIPv6Subnets); err != nil {
|
||||
errs = append(errs, err.(interface{ Unwrap() []error }).Unwrap()...)
|
||||
}
|
||||
}
|
||||
|
||||
if nw == nil {
|
||||
return multierror.Join(errs...)
|
||||
if err := multierror.Join(errs...); err != nil {
|
||||
return fmt.Errorf("invalid endpoint settings:\n%w", err)
|
||||
}
|
||||
|
||||
_, _, nwIPv4Configs, nwIPv6Configs := nw.IpamConfig()
|
||||
if err := validateEndpointIPAddress(nwIPv4Configs, ipamConfig.IPv4Address); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if err := validateEndpointIPAddress(nwIPv6Configs, ipamConfig.IPv6Address); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
return multierror.Join(errs...)
|
||||
}
|
||||
|
||||
func validateEndpointIPAddress(nwIPAMConfig []*libnetwork.IpamConf, epAddr string) error {
|
||||
if epAddr == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var customSubnet bool
|
||||
parsedAddr := net.ParseIP(epAddr)
|
||||
for _, conf := range nwIPAMConfig {
|
||||
if conf.PreferredPool != "" {
|
||||
customSubnet = true
|
||||
|
||||
_, allowedRange, _ := net.ParseCIDR(conf.PreferredPool)
|
||||
if conf.SubPool != "" {
|
||||
_, allowedRange, _ = net.ParseCIDR(conf.SubPool)
|
||||
}
|
||||
|
||||
if allowedRange.Contains(parsedAddr) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if customSubnet {
|
||||
return fmt.Errorf("no predefined subnet or ip-range contain the IP address: %s", epAddr)
|
||||
}
|
||||
return runconfig.ErrUnsupportedNetworkNoSubnetAndIP
|
||||
return nil
|
||||
}
|
||||
|
||||
// cleanOperationalData resets the operational data from the passed endpoint settings
|
||||
|
|
|
@ -71,6 +71,8 @@ type networkDBTable struct {
|
|||
}
|
||||
|
||||
// IpamConf contains all the ipam related configurations for a network
|
||||
//
|
||||
// TODO(aker): use proper net/* structs instead of string literals.
|
||||
type IpamConf struct {
|
||||
// PreferredPool is the master address pool for containers and network interfaces.
|
||||
PreferredPool string
|
||||
|
@ -92,6 +94,28 @@ func (c *IpamConf) Validate() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Contains checks whether the ipamSubnet contains [addr].
|
||||
func (c *IpamConf) Contains(addr net.IP) bool {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
if c.PreferredPool == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
_, allowedRange, _ := net.ParseCIDR(c.PreferredPool)
|
||||
if c.SubPool != "" {
|
||||
_, allowedRange, _ = net.ParseCIDR(c.SubPool)
|
||||
}
|
||||
|
||||
return allowedRange.Contains(addr)
|
||||
}
|
||||
|
||||
// IsStatic checks whether the subnet was statically allocated (ie. user-defined).
|
||||
func (c *IpamConf) IsStatic() bool {
|
||||
return c != nil && c.PreferredPool != ""
|
||||
}
|
||||
|
||||
// IpamInfo contains all the ipam related operational info for a network
|
||||
type IpamInfo struct {
|
||||
PoolID string
|
||||
|
|
Loading…
Add table
Reference in a new issue