mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-10 20:42:26 +00:00
kube-apiserver: move "public IP matches IP family" check to option validation
This commit is contained in:
parent
cb7acfd46e
commit
4149933ed2
@ -22,9 +22,13 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
genericoptions "k8s.io/apiserver/pkg/server/options"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
netutils "k8s.io/utils/net"
|
netutils "k8s.io/utils/net"
|
||||||
|
|
||||||
|
controlplaneapiserver "k8s.io/kubernetes/pkg/controlplane/apiserver/options"
|
||||||
|
"k8s.io/kubernetes/pkg/controlplane/reconcilers"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: Longer term we should read this from some config store, rather than a flag.
|
// TODO: Longer term we should read this from some config store, rather than a flag.
|
||||||
@ -102,6 +106,25 @@ func validateServiceNodePort(options Extra) []error {
|
|||||||
return errs
|
return errs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validatePublicIPServiceClusterIPRangeIPFamilies(extra Extra, generic genericoptions.ServerRunOptions) []error {
|
||||||
|
// The "kubernetes.default" Service is SingleStack based on the configured ServiceIPRange.
|
||||||
|
// If the bootstrap controller reconcile the kubernetes.default Service and Endpoints, it must
|
||||||
|
// guarantee that the Service ClusterIPRepairConfig and the associated Endpoints have the same IP family, or
|
||||||
|
// it will not work for clients because of the IP family mismatch.
|
||||||
|
// TODO: revisit for dual-stack https://github.com/kubernetes/enhancements/issues/2438
|
||||||
|
if reconcilers.Type(extra.EndpointReconcilerType) != reconcilers.NoneEndpointReconcilerType {
|
||||||
|
serviceIPRange, _, err := controlplaneapiserver.ServiceIPRange(extra.PrimaryServiceClusterIPRange)
|
||||||
|
if err != nil {
|
||||||
|
return []error{fmt.Errorf("error determining service IP ranges: %w", err)}
|
||||||
|
}
|
||||||
|
if netutils.IsIPv4CIDR(&serviceIPRange) != netutils.IsIPv4(generic.AdvertiseAddress) {
|
||||||
|
return []error{fmt.Errorf("service IP family %q must match public address family %q", extra.PrimaryServiceClusterIPRange.String(), generic.AdvertiseAddress.String())}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Validate checks ServerRunOptions and return a slice of found errs.
|
// Validate checks ServerRunOptions and return a slice of found errs.
|
||||||
func (s CompletedOptions) Validate() []error {
|
func (s CompletedOptions) Validate() []error {
|
||||||
var errs []error
|
var errs []error
|
||||||
@ -109,6 +132,7 @@ func (s CompletedOptions) Validate() []error {
|
|||||||
errs = append(errs, s.CompletedOptions.Validate()...)
|
errs = append(errs, s.CompletedOptions.Validate()...)
|
||||||
errs = append(errs, validateClusterIPFlags(s.Extra)...)
|
errs = append(errs, validateClusterIPFlags(s.Extra)...)
|
||||||
errs = append(errs, validateServiceNodePort(s.Extra)...)
|
errs = append(errs, validateServiceNodePort(s.Extra)...)
|
||||||
|
errs = append(errs, validatePublicIPServiceClusterIPRangeIPFamilies(s.Extra, *s.GenericServerRunOptions)...)
|
||||||
|
|
||||||
return errs
|
return errs
|
||||||
}
|
}
|
||||||
|
@ -21,10 +21,12 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||||
|
apiserveroptions "k8s.io/apiserver/pkg/server/options"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
netutils "k8s.io/utils/net"
|
netutils "k8s.io/utils/net"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
)
|
)
|
||||||
|
|
||||||
func makeOptionsWithCIDRs(serviceCIDR string, secondaryServiceCIDR string) *ServerRunOptions {
|
func makeOptionsWithCIDRs(serviceCIDR string, secondaryServiceCIDR string) *ServerRunOptions {
|
||||||
@ -155,6 +157,136 @@ func TestClusterServiceIPRange(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidatePublicIPServiceClusterIPRangeIPFamilies(t *testing.T) {
|
||||||
|
_, ipv4cidr, err := netutils.ParseCIDRSloppy("192.168.0.0/24")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ipv6cidr, err := netutils.ParseCIDRSloppy("2001:db8::/112")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ipv4address := netutils.ParseIPSloppy("192.168.1.1")
|
||||||
|
ipv6address := netutils.ParseIPSloppy("2001:db8::1")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
generic apiserveroptions.ServerRunOptions
|
||||||
|
extra Extra
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "master endpoint reconciler - IPv4 families",
|
||||||
|
extra: Extra{
|
||||||
|
EndpointReconcilerType: "master-count",
|
||||||
|
PrimaryServiceClusterIPRange: *ipv4cidr,
|
||||||
|
},
|
||||||
|
generic: apiserveroptions.ServerRunOptions{
|
||||||
|
AdvertiseAddress: ipv4address,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "master endpoint reconciler - IPv6 families",
|
||||||
|
extra: Extra{
|
||||||
|
EndpointReconcilerType: "master-count",
|
||||||
|
PrimaryServiceClusterIPRange: *ipv6cidr,
|
||||||
|
},
|
||||||
|
generic: apiserveroptions.ServerRunOptions{
|
||||||
|
AdvertiseAddress: ipv6address,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "master endpoint reconciler - wrong IP families",
|
||||||
|
extra: Extra{
|
||||||
|
EndpointReconcilerType: "master-count",
|
||||||
|
PrimaryServiceClusterIPRange: *ipv4cidr,
|
||||||
|
},
|
||||||
|
generic: apiserveroptions.ServerRunOptions{
|
||||||
|
AdvertiseAddress: ipv6address,
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "master endpoint reconciler - wrong IP families",
|
||||||
|
extra: Extra{
|
||||||
|
EndpointReconcilerType: "master-count",
|
||||||
|
PrimaryServiceClusterIPRange: *ipv6cidr,
|
||||||
|
},
|
||||||
|
generic: apiserveroptions.ServerRunOptions{
|
||||||
|
AdvertiseAddress: ipv4address,
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "lease endpoint reconciler - IPv4 families",
|
||||||
|
extra: Extra{
|
||||||
|
EndpointReconcilerType: "lease",
|
||||||
|
PrimaryServiceClusterIPRange: *ipv4cidr,
|
||||||
|
},
|
||||||
|
generic: apiserveroptions.ServerRunOptions{
|
||||||
|
AdvertiseAddress: ipv4address,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "lease endpoint reconciler - IPv6 families",
|
||||||
|
extra: Extra{
|
||||||
|
EndpointReconcilerType: "lease",
|
||||||
|
PrimaryServiceClusterIPRange: *ipv6cidr,
|
||||||
|
},
|
||||||
|
generic: apiserveroptions.ServerRunOptions{
|
||||||
|
AdvertiseAddress: ipv6address,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "lease endpoint reconciler - wrong IP families",
|
||||||
|
extra: Extra{
|
||||||
|
EndpointReconcilerType: "lease",
|
||||||
|
PrimaryServiceClusterIPRange: *ipv4cidr,
|
||||||
|
},
|
||||||
|
generic: apiserveroptions.ServerRunOptions{
|
||||||
|
AdvertiseAddress: ipv6address,
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "lease endpoint reconciler - wrong IP families",
|
||||||
|
extra: Extra{
|
||||||
|
EndpointReconcilerType: "lease",
|
||||||
|
PrimaryServiceClusterIPRange: *ipv6cidr,
|
||||||
|
},
|
||||||
|
generic: apiserveroptions.ServerRunOptions{
|
||||||
|
AdvertiseAddress: ipv4address,
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "none endpoint reconciler - wrong IP families",
|
||||||
|
extra: Extra{
|
||||||
|
EndpointReconcilerType: "none",
|
||||||
|
PrimaryServiceClusterIPRange: *ipv4cidr,
|
||||||
|
},
|
||||||
|
generic: apiserveroptions.ServerRunOptions{
|
||||||
|
AdvertiseAddress: ipv6address,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
errs := validatePublicIPServiceClusterIPRangeIPFamilies(tt.extra, tt.generic)
|
||||||
|
if (len(errs) > 0) != tt.wantErr {
|
||||||
|
t.Fatalf("completedConfig.New() errors = %+v, wantErr %v", errs, tt.wantErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func getIPnetFromCIDR(cidr string) *net.IPNet {
|
func getIPnetFromCIDR(cidr string) *net.IPNet {
|
||||||
_, ipnet, _ := netutils.ParseCIDRSloppy(cidr)
|
_, ipnet, _ := netutils.ParseCIDRSloppy(cidr)
|
||||||
return ipnet
|
return ipnet
|
||||||
|
@ -1,176 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2014 The Kubernetes Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package controlplane
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
|
||||||
"k8s.io/client-go/kubernetes"
|
|
||||||
netutils "k8s.io/utils/net"
|
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/controlplane/reconcilers"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_completedConfig_NewBootstrapController(t *testing.T) {
|
|
||||||
_, ipv4cidr, err := netutils.ParseCIDRSloppy("192.168.0.0/24")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected error %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, ipv6cidr, err := netutils.ParseCIDRSloppy("2001:db8::/112")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected error %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ipv4address := netutils.ParseIPSloppy("192.168.1.1")
|
|
||||||
ipv6address := netutils.ParseIPSloppy("2001:db8::1")
|
|
||||||
|
|
||||||
type args struct {
|
|
||||||
client kubernetes.Interface
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
config genericapiserver.Config
|
|
||||||
extraConfig *ExtraConfig
|
|
||||||
args args
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "master endpoint reconciler - IPv4 families",
|
|
||||||
extraConfig: &ExtraConfig{
|
|
||||||
EndpointReconcilerType: reconcilers.MasterCountReconcilerType,
|
|
||||||
ServiceIPRange: *ipv4cidr,
|
|
||||||
},
|
|
||||||
config: genericapiserver.Config{
|
|
||||||
PublicAddress: ipv4address,
|
|
||||||
SecureServing: &genericapiserver.SecureServingInfo{Listener: fakeLocalhost443Listener{}},
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "master endpoint reconciler - IPv6 families",
|
|
||||||
extraConfig: &ExtraConfig{
|
|
||||||
EndpointReconcilerType: reconcilers.MasterCountReconcilerType,
|
|
||||||
ServiceIPRange: *ipv6cidr,
|
|
||||||
},
|
|
||||||
config: genericapiserver.Config{
|
|
||||||
PublicAddress: ipv6address,
|
|
||||||
SecureServing: &genericapiserver.SecureServingInfo{Listener: fakeLocalhost443Listener{}},
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "master endpoint reconciler - wrong IP families",
|
|
||||||
extraConfig: &ExtraConfig{
|
|
||||||
EndpointReconcilerType: reconcilers.MasterCountReconcilerType,
|
|
||||||
ServiceIPRange: *ipv4cidr,
|
|
||||||
},
|
|
||||||
config: genericapiserver.Config{
|
|
||||||
PublicAddress: ipv6address,
|
|
||||||
SecureServing: &genericapiserver.SecureServingInfo{Listener: fakeLocalhost443Listener{}},
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "master endpoint reconciler - wrong IP families",
|
|
||||||
extraConfig: &ExtraConfig{
|
|
||||||
EndpointReconcilerType: reconcilers.MasterCountReconcilerType,
|
|
||||||
ServiceIPRange: *ipv6cidr,
|
|
||||||
},
|
|
||||||
config: genericapiserver.Config{
|
|
||||||
PublicAddress: ipv4address,
|
|
||||||
SecureServing: &genericapiserver.SecureServingInfo{Listener: fakeLocalhost443Listener{}},
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "lease endpoint reconciler - IPv4 families",
|
|
||||||
extraConfig: &ExtraConfig{
|
|
||||||
EndpointReconcilerType: reconcilers.LeaseEndpointReconcilerType,
|
|
||||||
ServiceIPRange: *ipv4cidr,
|
|
||||||
},
|
|
||||||
config: genericapiserver.Config{
|
|
||||||
PublicAddress: ipv4address,
|
|
||||||
SecureServing: &genericapiserver.SecureServingInfo{Listener: fakeLocalhost443Listener{}},
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "lease endpoint reconciler - IPv6 families",
|
|
||||||
extraConfig: &ExtraConfig{
|
|
||||||
EndpointReconcilerType: reconcilers.LeaseEndpointReconcilerType,
|
|
||||||
ServiceIPRange: *ipv6cidr,
|
|
||||||
},
|
|
||||||
config: genericapiserver.Config{
|
|
||||||
PublicAddress: ipv6address,
|
|
||||||
SecureServing: &genericapiserver.SecureServingInfo{Listener: fakeLocalhost443Listener{}},
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "lease endpoint reconciler - wrong IP families",
|
|
||||||
extraConfig: &ExtraConfig{
|
|
||||||
EndpointReconcilerType: reconcilers.LeaseEndpointReconcilerType,
|
|
||||||
ServiceIPRange: *ipv4cidr,
|
|
||||||
},
|
|
||||||
config: genericapiserver.Config{
|
|
||||||
PublicAddress: ipv6address,
|
|
||||||
SecureServing: &genericapiserver.SecureServingInfo{Listener: fakeLocalhost443Listener{}},
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "lease endpoint reconciler - wrong IP families",
|
|
||||||
extraConfig: &ExtraConfig{
|
|
||||||
EndpointReconcilerType: reconcilers.LeaseEndpointReconcilerType,
|
|
||||||
ServiceIPRange: *ipv6cidr,
|
|
||||||
},
|
|
||||||
config: genericapiserver.Config{
|
|
||||||
PublicAddress: ipv4address,
|
|
||||||
SecureServing: &genericapiserver.SecureServingInfo{Listener: fakeLocalhost443Listener{}},
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "none endpoint reconciler - wrong IP families",
|
|
||||||
extraConfig: &ExtraConfig{
|
|
||||||
EndpointReconcilerType: reconcilers.NoneEndpointReconcilerType,
|
|
||||||
ServiceIPRange: *ipv4cidr,
|
|
||||||
},
|
|
||||||
config: genericapiserver.Config{
|
|
||||||
PublicAddress: ipv6address,
|
|
||||||
SecureServing: &genericapiserver.SecureServingInfo{Listener: fakeLocalhost443Listener{}},
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
c := &completedConfig{
|
|
||||||
GenericConfig: tt.config.Complete(nil),
|
|
||||||
ExtraConfig: tt.extraConfig,
|
|
||||||
}
|
|
||||||
_, err := c.newKubernetesServiceControllerConfig(tt.args.client)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("completedConfig.NewBootstrapController() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -89,7 +89,6 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/routes"
|
"k8s.io/kubernetes/pkg/routes"
|
||||||
"k8s.io/kubernetes/pkg/serviceaccount"
|
"k8s.io/kubernetes/pkg/serviceaccount"
|
||||||
"k8s.io/utils/clock"
|
"k8s.io/utils/clock"
|
||||||
netutils "k8s.io/utils/net"
|
|
||||||
|
|
||||||
// RESTStorage installers
|
// RESTStorage installers
|
||||||
admissionregistrationrest "k8s.io/kubernetes/pkg/registry/admissionregistration/rest"
|
admissionregistrationrest "k8s.io/kubernetes/pkg/registry/admissionregistration/rest"
|
||||||
@ -631,17 +630,6 @@ func (c completedConfig) newKubernetesServiceControllerConfig(client kubernetes.
|
|||||||
return nil, fmt.Errorf("failed to get listener address: %w", err)
|
return nil, fmt.Errorf("failed to get listener address: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The "kubernetes.default" Service is SingleStack based on the configured ServiceIPRange.
|
|
||||||
// If the bootstrap controller reconcile the kubernetes.default Service and Endpoints, it must
|
|
||||||
// guarantee that the Service ClusterIP and the associated Endpoints have the same IP family, or
|
|
||||||
// it will not work for clients because of the IP family mismatch.
|
|
||||||
// TODO: revisit for dual-stack https://github.com/kubernetes/enhancements/issues/2438
|
|
||||||
if c.ExtraConfig.EndpointReconcilerType != reconcilers.NoneEndpointReconcilerType {
|
|
||||||
if netutils.IsIPv4CIDR(&c.ExtraConfig.ServiceIPRange) != netutils.IsIPv4(c.GenericConfig.PublicAddress) {
|
|
||||||
return nil, fmt.Errorf("service IP family %q must match public address family %q", c.ExtraConfig.ServiceIPRange.String(), c.GenericConfig.PublicAddress.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &kubernetesservice.Config{
|
return &kubernetesservice.Config{
|
||||||
Client: client,
|
Client: client,
|
||||||
Informers: c.ExtraConfig.VersionedInformers,
|
Informers: c.ExtraConfig.VersionedInformers,
|
||||||
|
Loading…
Reference in New Issue
Block a user