mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-26 21:17:23 +00:00
Merge pull request #125813 from aojea/node_csr_ips
Node Request Certificates require to have IPs
This commit is contained in:
commit
fa7fcde5a4
@ -45,6 +45,13 @@ const (
|
|||||||
// Enable usage of Provision of PVCs from snapshots in other namespaces
|
// Enable usage of Provision of PVCs from snapshots in other namespaces
|
||||||
CrossNamespaceVolumeDataSource featuregate.Feature = "CrossNamespaceVolumeDataSource"
|
CrossNamespaceVolumeDataSource featuregate.Feature = "CrossNamespaceVolumeDataSource"
|
||||||
|
|
||||||
|
// owner: @aojea
|
||||||
|
// Deprecated: v1.31
|
||||||
|
//
|
||||||
|
// Allow kubelet to request a certificate without any Node IP available, only
|
||||||
|
// with DNS names.
|
||||||
|
AllowDNSOnlyNodeCSR featuregate.Feature = "AllowDNSOnlyNodeCSR"
|
||||||
|
|
||||||
// owner: @thockin
|
// owner: @thockin
|
||||||
// deprecated: v1.29
|
// deprecated: v1.29
|
||||||
//
|
//
|
||||||
@ -991,6 +998,8 @@ func init() {
|
|||||||
var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
|
var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
|
||||||
CrossNamespaceVolumeDataSource: {Default: false, PreRelease: featuregate.Alpha},
|
CrossNamespaceVolumeDataSource: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
|
||||||
|
AllowDNSOnlyNodeCSR: {Default: false, PreRelease: featuregate.Deprecated}, // remove after 1.33
|
||||||
|
|
||||||
AllowServiceLBStatusOnNonLB: {Default: false, PreRelease: featuregate.Deprecated}, // remove after 1.29
|
AllowServiceLBStatusOnNonLB: {Default: false, PreRelease: featuregate.Deprecated}, // remove after 1.29
|
||||||
|
|
||||||
AnyVolumeDataSource: {Default: true, PreRelease: featuregate.Beta}, // on by default in 1.24
|
AnyVolumeDataSource: {Default: true, PreRelease: featuregate.Beta}, // on by default in 1.24
|
||||||
|
@ -32,16 +32,44 @@ import (
|
|||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apiserver/pkg/server/dynamiccertificates"
|
"k8s.io/apiserver/pkg/server/dynamiccertificates"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/util/certificate"
|
"k8s.io/client-go/util/certificate"
|
||||||
compbasemetrics "k8s.io/component-base/metrics"
|
compbasemetrics "k8s.io/component-base/metrics"
|
||||||
"k8s.io/component-base/metrics/legacyregistry"
|
"k8s.io/component-base/metrics/legacyregistry"
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
|
kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/metrics"
|
"k8s.io/kubernetes/pkg/kubelet/metrics"
|
||||||
netutils "k8s.io/utils/net"
|
netutils "k8s.io/utils/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func newGetTemplateFn(nodeName types.NodeName, getAddresses func() []v1.NodeAddress) func() *x509.CertificateRequest {
|
||||||
|
return func() *x509.CertificateRequest {
|
||||||
|
hostnames, ips := addressesToHostnamesAndIPs(getAddresses())
|
||||||
|
// by default, require at least one IP before requesting a serving certificate
|
||||||
|
hasRequiredAddresses := len(ips) > 0
|
||||||
|
|
||||||
|
// optionally allow requesting a serving certificate with just a DNS name
|
||||||
|
if utilfeature.DefaultFeatureGate.Enabled(features.AllowDNSOnlyNodeCSR) {
|
||||||
|
hasRequiredAddresses = hasRequiredAddresses || len(hostnames) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't return a template if we have no addresses to request for
|
||||||
|
if !hasRequiredAddresses {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &x509.CertificateRequest{
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: fmt.Sprintf("system:node:%s", nodeName),
|
||||||
|
Organization: []string{"system:nodes"},
|
||||||
|
},
|
||||||
|
DNSNames: hostnames,
|
||||||
|
IPAddresses: ips,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NewKubeletServerCertificateManager creates a certificate manager for the kubelet when retrieving a server certificate
|
// NewKubeletServerCertificateManager creates a certificate manager for the kubelet when retrieving a server certificate
|
||||||
// or returns an error.
|
// or returns an error.
|
||||||
func NewKubeletServerCertificateManager(kubeClient clientset.Interface, kubeCfg *kubeletconfig.KubeletConfiguration, nodeName types.NodeName, getAddresses func() []v1.NodeAddress, certDirectory string) (certificate.Manager, error) {
|
func NewKubeletServerCertificateManager(kubeClient clientset.Interface, kubeCfg *kubeletconfig.KubeletConfiguration, nodeName types.NodeName, getAddresses func() []v1.NodeAddress, certDirectory string) (certificate.Manager, error) {
|
||||||
@ -92,21 +120,7 @@ func NewKubeletServerCertificateManager(kubeClient clientset.Interface, kubeCfg
|
|||||||
)
|
)
|
||||||
legacyregistry.MustRegister(certificateRotationAge)
|
legacyregistry.MustRegister(certificateRotationAge)
|
||||||
|
|
||||||
getTemplate := func() *x509.CertificateRequest {
|
getTemplate := newGetTemplateFn(nodeName, getAddresses)
|
||||||
hostnames, ips := addressesToHostnamesAndIPs(getAddresses())
|
|
||||||
// don't return a template if we have no addresses to request for
|
|
||||||
if len(hostnames) == 0 && len(ips) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &x509.CertificateRequest{
|
|
||||||
Subject: pkix.Name{
|
|
||||||
CommonName: fmt.Sprintf("system:node:%s", nodeName),
|
|
||||||
Organization: []string{"system:nodes"},
|
|
||||||
},
|
|
||||||
DNSNames: hostnames,
|
|
||||||
IPAddresses: ips,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m, err := certificate.NewManager(&certificate.Config{
|
m, err := certificate.NewManager(&certificate.Config{
|
||||||
ClientsetFn: clientsetFn,
|
ClientsetFn: clientsetFn,
|
||||||
|
@ -19,6 +19,8 @@ package certificate
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
@ -28,8 +30,12 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
"k8s.io/client-go/util/cert"
|
"k8s.io/client-go/util/cert"
|
||||||
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
netutils "k8s.io/utils/net"
|
netutils "k8s.io/utils/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -261,3 +267,142 @@ func TestKubeletServerCertificateFromFiles(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewCertificateManagerConfigGetTemplate(t *testing.T) {
|
||||||
|
nodeName := "fake-node"
|
||||||
|
nodeIP := netutils.ParseIPSloppy("192.168.1.1")
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
nodeAddresses []v1.NodeAddress
|
||||||
|
want *x509.CertificateRequest
|
||||||
|
featuregate bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "node addresses or hostnames and gate enabled",
|
||||||
|
featuregate: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "node addresses or hostnames and gate disabled",
|
||||||
|
featuregate: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only hostnames and gate enabled",
|
||||||
|
nodeAddresses: []v1.NodeAddress{
|
||||||
|
{
|
||||||
|
Type: v1.NodeHostName,
|
||||||
|
Address: nodeName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: &x509.CertificateRequest{
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: fmt.Sprintf("system:node:%s", nodeName),
|
||||||
|
Organization: []string{"system:nodes"},
|
||||||
|
},
|
||||||
|
DNSNames: []string{nodeName},
|
||||||
|
},
|
||||||
|
featuregate: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only hostnames and gate disabled",
|
||||||
|
nodeAddresses: []v1.NodeAddress{
|
||||||
|
{
|
||||||
|
Type: v1.NodeHostName,
|
||||||
|
Address: nodeName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
featuregate: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only IP addresses and gate enabled",
|
||||||
|
nodeAddresses: []v1.NodeAddress{
|
||||||
|
{
|
||||||
|
Type: v1.NodeInternalIP,
|
||||||
|
Address: nodeIP.String(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: &x509.CertificateRequest{
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: fmt.Sprintf("system:node:%s", nodeName),
|
||||||
|
Organization: []string{"system:nodes"},
|
||||||
|
},
|
||||||
|
IPAddresses: []net.IP{nodeIP},
|
||||||
|
},
|
||||||
|
featuregate: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only IP addresses and gate disabled",
|
||||||
|
nodeAddresses: []v1.NodeAddress{
|
||||||
|
{
|
||||||
|
Type: v1.NodeInternalIP,
|
||||||
|
Address: nodeIP.String(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: &x509.CertificateRequest{
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: fmt.Sprintf("system:node:%s", nodeName),
|
||||||
|
Organization: []string{"system:nodes"},
|
||||||
|
},
|
||||||
|
IPAddresses: []net.IP{nodeIP},
|
||||||
|
},
|
||||||
|
featuregate: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "IP addresses and hostnames and gate enabled",
|
||||||
|
nodeAddresses: []v1.NodeAddress{
|
||||||
|
{
|
||||||
|
Type: v1.NodeHostName,
|
||||||
|
Address: nodeName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: v1.NodeInternalIP,
|
||||||
|
Address: nodeIP.String(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: &x509.CertificateRequest{
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: fmt.Sprintf("system:node:%s", nodeName),
|
||||||
|
Organization: []string{"system:nodes"},
|
||||||
|
},
|
||||||
|
DNSNames: []string{nodeName},
|
||||||
|
IPAddresses: []net.IP{nodeIP},
|
||||||
|
},
|
||||||
|
featuregate: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "IP addresses and hostnames and gate disabled",
|
||||||
|
nodeAddresses: []v1.NodeAddress{
|
||||||
|
{
|
||||||
|
Type: v1.NodeHostName,
|
||||||
|
Address: nodeName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: v1.NodeInternalIP,
|
||||||
|
Address: nodeIP.String(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: &x509.CertificateRequest{
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: fmt.Sprintf("system:node:%s", nodeName),
|
||||||
|
Organization: []string{"system:nodes"},
|
||||||
|
},
|
||||||
|
DNSNames: []string{nodeName},
|
||||||
|
IPAddresses: []net.IP{nodeIP},
|
||||||
|
},
|
||||||
|
featuregate: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.AllowDNSOnlyNodeCSR, tt.featuregate)
|
||||||
|
getAddresses := func() []v1.NodeAddress {
|
||||||
|
return tt.nodeAddresses
|
||||||
|
}
|
||||||
|
getTemplate := newGetTemplateFn(types.NodeName(nodeName), getAddresses)
|
||||||
|
got := getTemplate()
|
||||||
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("Wrong certificate, got %v expected %v", got, tt.want)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user