diff --git a/internal/app/machined/pkg/system/services/bootkube.go b/internal/app/machined/pkg/system/services/bootkube.go index 7a035f7fd..a7791665d 100644 --- a/internal/app/machined/pkg/system/services/bootkube.go +++ b/internal/app/machined/pkg/system/services/bootkube.go @@ -23,6 +23,7 @@ import ( "github.com/talos-systems/talos/internal/app/machined/pkg/system/runner/goroutine" "github.com/talos-systems/talos/pkg/config" "github.com/talos-systems/talos/pkg/constants" + tnet "github.com/talos-systems/talos/pkg/net" ) // Bootkube implements the Service interface. It serves as the concrete type with @@ -125,11 +126,11 @@ func generateAssets(config config.Configurator) (err error) { } apiServers = append(apiServers, u) - _, podCIDR, err := net.ParseCIDR("10.2.0.0/16") + _, podCIDR, err := net.ParseCIDR(config.Cluster().Network().PodCIDR()) if err != nil { return err } - _, serviceCIDR, err := net.ParseCIDR("10.3.0.0/24") + _, serviceCIDR, err := net.ParseCIDR(config.Cluster().Network().ServiceCIDR()) if err != nil { return err } @@ -154,6 +155,16 @@ func generateAssets(config config.Configurator) (err error) { return errors.Wrap(err, "failed to parse Kubernetes key") } + apiServiceIP, err := tnet.NthIPInNetwork(serviceCIDR, 1) + if err != nil { + return err + } + + dnsServiceIP, err := tnet.NthIPInNetwork(serviceCIDR, 10) + if err != nil { + return err + } + conf := asset.Config{ CACert: k8sCA, CAPrivKey: k8sKey, @@ -163,11 +174,11 @@ func generateAssets(config config.Configurator) (err error) { EtcdServers: []*url.URL{etcdServer}, EtcdUseTLS: true, APIServers: apiServers, - APIServiceIP: net.ParseIP("10.3.0.1"), - DNSServiceIP: net.ParseIP("10.3.0.10"), + APIServiceIP: apiServiceIP, + DNSServiceIP: dnsServiceIP, PodCIDR: podCIDR, ServiceCIDR: serviceCIDR, - NetworkProvider: "flannel", + NetworkProvider: config.Cluster().Network().CNI(), AltNames: altNames, Images: asset.DefaultImages, BootstrapSecretsSubdir: "/assets/tls", diff --git a/pkg/config/cluster/cluster.go b/pkg/config/cluster/cluster.go index dfe0606f4..0cf20a378 100644 --- a/pkg/config/cluster/cluster.go +++ b/pkg/config/cluster/cluster.go @@ -20,6 +20,15 @@ type Cluster interface { AESCBCEncryptionSecret() string Config(machine.Type) (string, error) Etcd() Etcd + Network() Network +} + +// Network defines the requirements for a config that pertains to cluster +// network options. +type Network interface { + CNI() string + PodCIDR() string + ServiceCIDR() string } // Etcd defines the requirements for a config that pertains to etcd related diff --git a/pkg/config/types/v1alpha1/cluster_config.go b/pkg/config/types/v1alpha1/cluster_config.go index 3938b5ff3..532dfe716 100644 --- a/pkg/config/types/v1alpha1/cluster_config.go +++ b/pkg/config/types/v1alpha1/cluster_config.go @@ -9,6 +9,7 @@ import ( "github.com/talos-systems/talos/pkg/config/cluster" "github.com/talos-systems/talos/pkg/config/machine" + "github.com/talos-systems/talos/pkg/constants" "github.com/talos-systems/talos/pkg/crypto/x509" ) @@ -16,7 +17,7 @@ import ( type ClusterConfig struct { ControlPlane *ControlPlaneConfig `yaml:"controlPlane"` ClusterName string `yaml:"clusterName,omitempty"` - Network *ClusterNetworkConfig `yaml:"network,omitempty"` + ClusterNetwork *ClusterNetworkConfig `yaml:"network,omitempty"` BootstrapToken string `yaml:"token,omitempty"` CertificateKey string `yaml:"certificateKey"` ClusterAESCBCEncryptionSecret string `yaml:"aescbcEncryptionSecret"` @@ -67,6 +68,7 @@ type EtcdConfig struct { // ClusterNetworkConfig represents kube networking config vals type ClusterNetworkConfig struct { + CNI string `yaml:"cni"` DNSDomain string `yaml:"dnsDomain"` PodSubnet []string `yaml:"podSubnets"` ServiceSubnet []string `yaml:"serviceSubnets"` @@ -139,3 +141,35 @@ func (c *ClusterConfig) Secret() string { } return parts[1] } + +// Network implements the Configurator interface. +func (c *ClusterConfig) Network() cluster.Network { + return c +} + +// CNI implements the Configurator interface. +func (c *ClusterConfig) CNI() string { + if c.ClusterNetwork.CNI == "" { + return constants.DefaultCNI + } + + return c.ClusterNetwork.CNI +} + +// PodCIDR implements the Configurator interface. +func (c *ClusterConfig) PodCIDR() string { + if len(c.ClusterNetwork.PodSubnet) == 0 { + return constants.DefaultPodCIDR + } + + return c.ClusterNetwork.PodSubnet[0] +} + +// ServiceCIDR implements the Configurator interface. +func (c *ClusterConfig) ServiceCIDR() string { + if len(c.ClusterNetwork.ServiceSubnet) == 0 { + return constants.DefaultServiceCIDR + } + + return c.ClusterNetwork.ServiceSubnet[0] +} diff --git a/pkg/config/types/v1alpha1/generate/init.go b/pkg/config/types/v1alpha1/generate/init.go index 1e393d515..3229e9d9e 100644 --- a/pkg/config/types/v1alpha1/generate/init.go +++ b/pkg/config/types/v1alpha1/generate/init.go @@ -37,7 +37,7 @@ func initUd(in *Input) (string, error) { EtcdConfig: &v1alpha1.EtcdConfig{ RootCA: in.Certs.Etcd, }, - Network: &v1alpha1.ClusterNetworkConfig{ + ClusterNetwork: &v1alpha1.ClusterNetworkConfig{ DNSDomain: in.ServiceDomain, PodSubnet: in.PodNet, ServiceSubnet: in.ServiceNet, diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index d75aeedbc..001ce1853 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -233,6 +233,15 @@ const ( // DefaultLogPath is the default path to the log storage directory. DefaultLogPath = SystemRunPath + "/log" + + // DefaultCNI is the default CNI. + DefaultCNI = "flannel" + + // DefaultPodCIDR is the default pod CIDR block. + DefaultPodCIDR = "10.244.0.0/16" + + // DefaultServiceCIDR is the default service CIDR block. + DefaultServiceCIDR = "10.96.0.0/12" ) // See https://linux.die.net/man/3/klogctl diff --git a/pkg/net/net.go b/pkg/net/net.go index 352acd22d..c34e6e580 100644 --- a/pkg/net/net.go +++ b/pkg/net/net.go @@ -6,6 +6,8 @@ package net import ( "net" + + "github.com/pkg/errors" ) // IPAddrs finds and returns a list of non-loopback IPv4 addresses of the @@ -40,3 +42,24 @@ func FormatAddress(addr string) string { } return addr } + +// NthIPInNetwork takes an IPNet and returns the nth IP in it. +func NthIPInNetwork(network *net.IPNet, n int) (net.IP, error) { + ip := network.IP + dst := make([]byte, len(ip)) + copy(dst, ip) + for i := 0; i < n; i++ { + for j := len(dst) - 1; j >= 0; j-- { + dst[j]++ + if dst[j] > 0 { + break + } + } + } + + if network.Contains(dst) { + return dst, nil + } + + return nil, errors.New("network does not contain enough IPs") +} diff --git a/pkg/net/net_test.go b/pkg/net/net_test.go index 3f1024d1d..24c6f86b7 100644 --- a/pkg/net/net_test.go +++ b/pkg/net/net_test.go @@ -5,6 +5,8 @@ package net import ( + "net" + "reflect" "testing" "gotest.tools/assert" @@ -23,3 +25,73 @@ func TestFormatAddress(t *testing.T) { assert.Equal(t, FormatAddress("192.168.1.1"), "192.168.1.1") assert.Equal(t, FormatAddress("alpha.beta.gamma.com"), "alpha.beta.gamma.com") } + +// nolint: scopelint +func TestNthIPInNetwork(t *testing.T) { + type args struct { + network *net.IPNet + n int + } + tests := []struct { + name string + args args + want net.IP + }{ + { + name: "increment IPv4 by 1", + args: args{ + network: &net.IPNet{ + IP: net.IP{10, 96, 0, 0}, + Mask: net.IPMask{255, 255, 255, 0}, + }, + n: 1, + }, + want: net.IP{10, 96, 0, 1}, + }, + { + name: "increment IPv4 by 10", + args: args{ + network: &net.IPNet{ + IP: net.IP{10, 96, 0, 0}, + Mask: net.IPMask{255, 255, 255, 0}, + }, + n: 10, + }, + want: net.IP{10, 96, 0, 10}, + }, + { + name: "increment IPv6 by 1", + args: args{ + network: &net.IPNet{ + IP: net.ParseIP("2001:db8:a0b:12f0::1"), + Mask: net.IPMask{255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + n: 1, + }, + want: net.ParseIP("2001:db8:a0b:12f0::2"), + }, + { + name: "increment IPv6 by 10", + args: args{ + network: &net.IPNet{ + IP: net.ParseIP("2001:db8:a0b:12f0::1"), + Mask: net.IPMask{255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + n: 10, + }, + want: net.ParseIP("2001:db8:a0b:12f0::b"), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := NthIPInNetwork(tt.args.network, tt.args.n) + if err != nil { + t.Errorf("%v", err) + } + + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("NthFromIP() = %v, want %v", got, tt.want) + } + }) + } +}