github.com/webmeshproj/webmesh-cni@v0.0.27/internal/host/config.go (about)

     1  /*
     2  Copyright 2023 Avi Zimmerman <avi.zimmerman@gmail.com>.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package host
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"net/netip"
    23  	"os"
    24  	"strings"
    25  	"time"
    26  
    27  	"github.com/spf13/pflag"
    28  	"github.com/webmeshproj/webmesh/pkg/config"
    29  	"github.com/webmeshproj/webmesh/pkg/meshnet/endpoints"
    30  
    31  	"github.com/webmeshproj/webmesh-cni/internal/types"
    32  )
    33  
    34  // Config contains the options for the host node.
    35  type Config struct {
    36  	// NodeID is the ID of the node.
    37  	NodeID string `koanf:"node-id"`
    38  	// Namespace is the namespace of the node.
    39  	Namespace string `koanf:"namespace,omitempty"`
    40  	// LockDuration is the duration to hold locks for when allocating addresses.
    41  	LockDuration time.Duration `koanf:"lock-duration,omitempty"`
    42  	// LockAcquireTimeout is the timeout for acquiring locks when allocating addresses.
    43  	LockAcquireTimeout time.Duration `koanf:"lock-acquire-timeout,omitempty"`
    44  	// ConnectTimeout is the timeout for connecting the host webmesh node to the network.
    45  	ConnectTimeout time.Duration `koanf:"connect-timeout,omitempty"`
    46  	// Auth are configuration options for authenticating with other nodes.
    47  	Auth config.AuthOptions `koanf:"auth,omitempty"`
    48  	// WireGuard are configurations for the WireGuard interface.
    49  	WireGuard config.WireGuardOptions `koanf:"wireguard,omitempty"`
    50  	// Services is the service options for the host webmesh node.
    51  	Services config.ServiceOptions `koanf:"services,omitempty"`
    52  	// Plugins is the plugin options for the host webmesh node.
    53  	Plugins config.PluginOptions `koanf:"plugins,omitempty"`
    54  	// Network is the network options for the host webmesh node.
    55  	Network NetworkConfig `koanf:"network,omitempty"`
    56  	// LogLevel is the log level for the host webmesh node.
    57  	LogLevel string `koanf:"log-level,omitempty"`
    58  }
    59  
    60  // NewDefaultConfig returns a new default configuration for the host webmesh node.
    61  func NewDefaultConfig() Config {
    62  	c := Config{
    63  		NodeID:             os.Getenv(types.NodeNameEnvVar),
    64  		Namespace:          os.Getenv(types.PodNamespaceEnvVar),
    65  		LockDuration:       time.Second * 10,
    66  		LockAcquireTimeout: time.Second * 5,
    67  		ConnectTimeout:     time.Second * 30,
    68  		Auth:               config.NewAuthOptions(),
    69  		WireGuard:          config.NewWireGuardOptions(),
    70  		Services:           config.NewServiceOptions(true),
    71  		Plugins:            config.NewPluginOptions(),
    72  		Network:            NewNetworkConfig(),
    73  		LogLevel:           "info",
    74  	}
    75  	// Make full-tunnel opt-in on the host node. It would almost certainly
    76  	// break things if it were enabled by default.
    77  	c.WireGuard.DisableFullTunnel = true
    78  	return c
    79  }
    80  
    81  func (o *Config) BindFlags(prefix string, fs *pflag.FlagSet) {
    82  	fs.StringVar(&o.NodeID, prefix+"node-id", o.NodeID, "The ID of the node")
    83  	fs.StringVar(&o.Namespace, prefix+"namespace", o.Namespace, "The namespace of the node")
    84  	fs.DurationVar(&o.LockDuration, prefix+"lock-duration", o.LockDuration, "The duration to hold locks for when allocating addresses")
    85  	fs.DurationVar(&o.LockAcquireTimeout, prefix+"lock-acquire-timeout", o.LockAcquireTimeout, "The timeout for acquiring locks when allocating addresses")
    86  	fs.DurationVar(&o.ConnectTimeout, prefix+"connect-timeout", o.ConnectTimeout, "The timeout for connecting the host webmesh node to the network")
    87  	fs.StringVar(&o.LogLevel, prefix+"log-level", o.LogLevel, "The log level for the host webmesh node")
    88  	o.Auth.BindFlags(prefix+"auth.", fs)
    89  	o.WireGuard.BindFlags(prefix+"wireguard.", fs)
    90  	o.Network.BindFlags(prefix+"network.", fs)
    91  	o.Services.BindFlags(prefix+"services.", fs)
    92  	o.Plugins.BindFlags(prefix+"plugins.", fs)
    93  }
    94  
    95  func (o *Config) Validate() error {
    96  	if o.NodeID == "" {
    97  		return errors.New("node-id must be set")
    98  	}
    99  	if o.Namespace == "" {
   100  		return errors.New("namespace must be set")
   101  	}
   102  	if o.ConnectTimeout <= 0 {
   103  		return errors.New("connect-timeout must be positive")
   104  	}
   105  	if o.LockDuration <= 0 {
   106  		return errors.New("lock-duration must be positive")
   107  	}
   108  	if o.LockAcquireTimeout <= 0 {
   109  		return errors.New("lock-acquire-timeout must be positive")
   110  	}
   111  	if err := o.Network.Validate(); err != nil {
   112  		return err
   113  	}
   114  	if err := o.Services.Validate(); err != nil {
   115  		return err
   116  	}
   117  	if err := o.WireGuard.Validate(); err != nil {
   118  		return err
   119  	}
   120  	return nil
   121  }
   122  
   123  // NetworkConfig contains the options for the network.
   124  type NetworkConfig struct {
   125  	// RemoteEndpointDetection enables remote endpoint detection for wireguard endpoints.
   126  	RemoteEndpointDetection bool `koanf:"remote-endpoint-detection,omitempty"`
   127  	// PodCIDR is a comma separated list of CIDRs to use for the pod network.
   128  	// If no IPv6 CIDR is provided, one will be generated.
   129  	PodCIDR string `koanf:"pod-cidr,omitempty"`
   130  	// ServiceCIDR is a comma-separated list of CIDRs to use for the service network.
   131  	ServiceCIDR string `koanf:"service-cidr,omitempty"`
   132  	// ClusterDomain is the cluster domain to use for the network.
   133  	ClusterDomain string `koanf:"cluster-domain,omitempty"`
   134  	// Routes to allow for container and other connected node traffic.
   135  	Routes []string `koanf:"routes,omitempty"`
   136  	// WriteResolvConf will add any MeshDNS servers to the system resolv.conf.
   137  	WriteResolvConf bool `koanf:"write-resolv-conf,omitempty"`
   138  	// DisableIPv4 disables IPv4 on the host webmesh node.
   139  	DisableIPv4 bool `koanf:"disable-ipv4,omitempty"`
   140  	// DisableIPv6 disables IPv6 on the host webmesh node.
   141  	DisableIPv6 bool `koanf:"disable-ipv6,omitempty"`
   142  	// DisableRBAC disables RBAC controls on the webmesh network.
   143  	// This only takes during initial cluster bootstrap.
   144  	DisableRBAC bool `koanf:"disable-rbac,omitempty"`
   145  }
   146  
   147  // PodCIDRs returns the pod CIDRs.
   148  func (n *NetworkConfig) PodCIDRs() endpoints.PrefixList {
   149  	var cidrs []netip.Prefix
   150  	if n.PodCIDR == "" {
   151  		return cidrs
   152  	}
   153  	for _, cidr := range strings.Split(n.PodCIDR, ",") {
   154  		cidrs = append(cidrs, netip.MustParsePrefix(cidr))
   155  	}
   156  	return cidrs
   157  }
   158  
   159  // ServiceCIDRs returns the service CIDRs.
   160  func (n *NetworkConfig) ServiceCIDRs() endpoints.PrefixList {
   161  	var cidrs []netip.Prefix
   162  	if n.ServiceCIDR == "" {
   163  		return cidrs
   164  	}
   165  	for _, cidr := range strings.Split(n.ServiceCIDR, ",") {
   166  		cidrs = append(cidrs, netip.MustParsePrefix(cidr))
   167  	}
   168  	return cidrs
   169  }
   170  
   171  // CIDRs returns all CIDRs.
   172  func (n *NetworkConfig) CIDRs() endpoints.PrefixList {
   173  	return append(n.PodCIDRs(), n.ServiceCIDRs()...)
   174  }
   175  
   176  // CIDRsContain checks if the local CIDRs contain the given prefix.
   177  func (n *NetworkConfig) CIDRsContain(prefix netip.Prefix) bool {
   178  	for _, cidr := range n.PodCIDRs() {
   179  		if cidr.Contains(prefix.Addr()) && prefix.Bits() >= cidr.Bits() {
   180  			return true
   181  		}
   182  	}
   183  	for _, cidr := range n.ServiceCIDRs() {
   184  		if cidr.Contains(prefix.Addr()) && prefix.Bits() >= cidr.Bits() {
   185  			return true
   186  		}
   187  	}
   188  	return false
   189  }
   190  
   191  func NewNetworkConfig() NetworkConfig {
   192  	return NetworkConfig{
   193  		RemoteEndpointDetection: false,
   194  		PodCIDR:                 os.Getenv(types.PodCIDREnvVar),
   195  		ServiceCIDR:             os.Getenv(types.ServiceCIDREnvVar),
   196  		ClusterDomain:           os.Getenv(types.ClusterDomainEnvVar),
   197  		Routes:                  []string{"0.0.0.0/0", "::/0"},
   198  		DisableIPv4:             false,
   199  		DisableIPv6:             false,
   200  		DisableRBAC:             true,
   201  	}
   202  }
   203  
   204  func (n *NetworkConfig) BindFlags(prefix string, fs *pflag.FlagSet) {
   205  	fs.BoolVar(&n.RemoteEndpointDetection, prefix+"remote-endpoint-detection", n.RemoteEndpointDetection, "Enable remote endpoint detection for wireguard endpoints")
   206  	fs.StringVar(&n.PodCIDR, prefix+"pod-cidr", n.PodCIDR, "The CIDR(s) to use for the pod network")
   207  	fs.StringVar(&n.ServiceCIDR, prefix+"service-cidr", n.ServiceCIDR, "The CIDR(s) to use for the service network")
   208  	fs.StringVar(&n.ClusterDomain, prefix+"cluster-domain", n.ClusterDomain, "The cluster domain to use for the network")
   209  	fs.StringSliceVar(&n.Routes, prefix+"routes", n.Routes, "Routes to allow for container and other connected node traffic")
   210  	fs.BoolVar(&n.WriteResolvConf, prefix+"write-resolv-conf", n.WriteResolvConf, "Write MeshDNS servers to the local resolv.conf")
   211  	fs.BoolVar(&n.DisableIPv4, prefix+"disable-ipv4", n.DisableIPv4, "Disable IPv4 on the host webmesh node")
   212  	fs.BoolVar(&n.DisableIPv6, prefix+"disable-ipv6", n.DisableIPv6, "Disable IPv6 on the host webmesh node")
   213  	fs.BoolVar(&n.DisableRBAC, prefix+"disable-rbac", n.DisableRBAC, "Disable RBAC controls on the webmesh network")
   214  }
   215  
   216  func (n *NetworkConfig) Validate() error {
   217  	if n.PodCIDR == "" {
   218  		return errors.New("pod-cidr must be set")
   219  	}
   220  	for _, addr := range strings.Split(n.PodCIDR, ",") {
   221  		_, err := netip.ParsePrefix(addr)
   222  		if err != nil {
   223  			return fmt.Errorf("invalid pod-cidr: %w", err)
   224  		}
   225  	}
   226  	if n.ServiceCIDR != "" {
   227  		for _, addr := range strings.Split(n.ServiceCIDR, ",") {
   228  			_, err := netip.ParsePrefix(addr)
   229  			if err != nil {
   230  				return fmt.Errorf("invalid service-cidr: %w", err)
   231  			}
   232  		}
   233  	}
   234  	if n.ClusterDomain == "" {
   235  		return errors.New("cluster-domain must be set")
   236  	}
   237  	if len(n.Routes) == 0 {
   238  		return errors.New("at least one route must be set")
   239  	}
   240  	for _, rt := range n.Routes {
   241  		_, err := netip.ParsePrefix(rt)
   242  		if err != nil {
   243  			return fmt.Errorf("invalid route: %w", err)
   244  		}
   245  	}
   246  	if n.DisableIPv4 && n.DisableIPv6 {
   247  		return errors.New("cannot disable both IPv4 and IPv6")
   248  	}
   249  	return nil
   250  }