From 4f1ad16c764e643f7bf71ed8ca46e840875011ec Mon Sep 17 00:00:00 2001 From: Noel Georgi Date: Tue, 7 Nov 2023 10:55:45 +0530 Subject: [PATCH] feat: support kubelet credentialprovider config Support configuring kubelet credential provider config. Partially fixes: #7911 Signed-off-by: Noel Georgi --- Dockerfile | 4 +- Makefile | 5 +- api/resource/definitions/k8s/k8s.proto | 2 + hack/release.toml | 9 + .../pkg/controllers/k8s/kubelet_config.go | 1 + .../pkg/controllers/k8s/kubelet_service.go | 33 ++ .../pkg/controllers/k8s/kubelet_spec.go | 9 +- .../machined/pkg/system/services/kubelet.go | 1 + .../api/resource/definitions/k8s/k8s.pb.go | 395 ++++++++++-------- .../definitions/k8s/k8s_vtproto.pb.go | 152 +++++++ pkg/machinery/config/config/machine.go | 3 + .../schemas/v1alpha1_config.schema.json | 7 + .../types/v1alpha1/v1alpha1_examples.go | 23 + .../types/v1alpha1/v1alpha1_provider.go | 5 + .../config/types/v1alpha1/v1alpha1_types.go | 7 + .../types/v1alpha1/v1alpha1_types_doc.go | 10 +- .../types/v1alpha1/zz_generated.deepcopy.go | 1 + pkg/machinery/constants/constants.go | 25 +- .../resources/k8s/deep_copy.generated.go | 12 + pkg/machinery/resources/k8s/kubelet_config.go | 1 + pkg/machinery/resources/k8s/kubelet_spec.go | 11 +- website/content/v1.6/reference/api.md | 2 + .../content/v1.6/reference/configuration.md | 45 ++ .../v1.6/schemas/v1alpha1_config.schema.json | 7 + 24 files changed, 569 insertions(+), 201 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8eef382bc0..10733a437e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -568,7 +568,7 @@ COPY ./hack/cleanup.sh /toolchain/bin/cleanup.sh RUN < talos.resource.definitions.k8s.KubeletConfigSpec.ExtraArgsEntry 46, // 17: talos.resource.definitions.k8s.KubeletConfigSpec.extra_mounts:type_name -> talos.resource.definitions.proto.Mount 44, // 18: talos.resource.definitions.k8s.KubeletConfigSpec.extra_config:type_name -> google.protobuf.Struct - 46, // 19: talos.resource.definitions.k8s.KubeletSpecSpec.extra_mounts:type_name -> talos.resource.definitions.proto.Mount - 44, // 20: talos.resource.definitions.k8s.KubeletSpecSpec.config:type_name -> google.protobuf.Struct - 28, // 21: talos.resource.definitions.k8s.ManifestSpec.items:type_name -> talos.resource.definitions.k8s.SingleManifest - 45, // 22: talos.resource.definitions.k8s.NodeIPSpec.addresses:type_name -> common.NetIP - 38, // 23: talos.resource.definitions.k8s.NodeStatusSpec.labels:type_name -> talos.resource.definitions.k8s.NodeStatusSpec.LabelsEntry - 39, // 24: talos.resource.definitions.k8s.NodeStatusSpec.annotations:type_name -> talos.resource.definitions.k8s.NodeStatusSpec.AnnotationsEntry - 40, // 25: talos.resource.definitions.k8s.Resources.requests:type_name -> talos.resource.definitions.k8s.Resources.RequestsEntry - 41, // 26: talos.resource.definitions.k8s.Resources.limits:type_name -> talos.resource.definitions.k8s.Resources.LimitsEntry - 42, // 27: talos.resource.definitions.k8s.SchedulerConfigSpec.extra_args:type_name -> talos.resource.definitions.k8s.SchedulerConfigSpec.ExtraArgsEntry - 10, // 28: talos.resource.definitions.k8s.SchedulerConfigSpec.extra_volumes:type_name -> talos.resource.definitions.k8s.ExtraVolume - 43, // 29: talos.resource.definitions.k8s.SchedulerConfigSpec.environment_variables:type_name -> talos.resource.definitions.k8s.SchedulerConfigSpec.EnvironmentVariablesEntry - 25, // 30: talos.resource.definitions.k8s.SchedulerConfigSpec.resources:type_name -> talos.resource.definitions.k8s.Resources - 44, // 31: talos.resource.definitions.k8s.SingleManifest.object:type_name -> google.protobuf.Struct - 44, // 32: talos.resource.definitions.k8s.StaticPodSpec.pod:type_name -> google.protobuf.Struct - 44, // 33: talos.resource.definitions.k8s.StaticPodStatusSpec.pod_status:type_name -> google.protobuf.Struct - 34, // [34:34] is the sub-list for method output_type - 34, // [34:34] is the sub-list for method input_type - 34, // [34:34] is the sub-list for extension type_name - 34, // [34:34] is the sub-list for extension extendee - 0, // [0:34] is the sub-list for field type_name + 44, // 19: talos.resource.definitions.k8s.KubeletConfigSpec.credential_provider_config:type_name -> google.protobuf.Struct + 46, // 20: talos.resource.definitions.k8s.KubeletSpecSpec.extra_mounts:type_name -> talos.resource.definitions.proto.Mount + 44, // 21: talos.resource.definitions.k8s.KubeletSpecSpec.config:type_name -> google.protobuf.Struct + 44, // 22: talos.resource.definitions.k8s.KubeletSpecSpec.credential_provider_config:type_name -> google.protobuf.Struct + 28, // 23: talos.resource.definitions.k8s.ManifestSpec.items:type_name -> talos.resource.definitions.k8s.SingleManifest + 45, // 24: talos.resource.definitions.k8s.NodeIPSpec.addresses:type_name -> common.NetIP + 38, // 25: talos.resource.definitions.k8s.NodeStatusSpec.labels:type_name -> talos.resource.definitions.k8s.NodeStatusSpec.LabelsEntry + 39, // 26: talos.resource.definitions.k8s.NodeStatusSpec.annotations:type_name -> talos.resource.definitions.k8s.NodeStatusSpec.AnnotationsEntry + 40, // 27: talos.resource.definitions.k8s.Resources.requests:type_name -> talos.resource.definitions.k8s.Resources.RequestsEntry + 41, // 28: talos.resource.definitions.k8s.Resources.limits:type_name -> talos.resource.definitions.k8s.Resources.LimitsEntry + 42, // 29: talos.resource.definitions.k8s.SchedulerConfigSpec.extra_args:type_name -> talos.resource.definitions.k8s.SchedulerConfigSpec.ExtraArgsEntry + 10, // 30: talos.resource.definitions.k8s.SchedulerConfigSpec.extra_volumes:type_name -> talos.resource.definitions.k8s.ExtraVolume + 43, // 31: talos.resource.definitions.k8s.SchedulerConfigSpec.environment_variables:type_name -> talos.resource.definitions.k8s.SchedulerConfigSpec.EnvironmentVariablesEntry + 25, // 32: talos.resource.definitions.k8s.SchedulerConfigSpec.resources:type_name -> talos.resource.definitions.k8s.Resources + 44, // 33: talos.resource.definitions.k8s.SingleManifest.object:type_name -> google.protobuf.Struct + 44, // 34: talos.resource.definitions.k8s.StaticPodSpec.pod:type_name -> google.protobuf.Struct + 44, // 35: talos.resource.definitions.k8s.StaticPodStatusSpec.pod_status:type_name -> google.protobuf.Struct + 36, // [36:36] is the sub-list for method output_type + 36, // [36:36] is the sub-list for method input_type + 36, // [36:36] is the sub-list for extension type_name + 36, // [36:36] is the sub-list for extension extendee + 0, // [0:36] is the sub-list for field type_name } func init() { file_resource_definitions_k8s_k8s_proto_init() } diff --git a/pkg/machinery/api/resource/definitions/k8s/k8s_vtproto.pb.go b/pkg/machinery/api/resource/definitions/k8s/k8s_vtproto.pb.go index 0fe3638312..dcae0b43f7 100644 --- a/pkg/machinery/api/resource/definitions/k8s/k8s_vtproto.pb.go +++ b/pkg/machinery/api/resource/definitions/k8s/k8s_vtproto.pb.go @@ -1155,6 +1155,28 @@ func (m *KubeletConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if m.CredentialProviderConfig != nil { + if vtmsg, ok := interface{}(m.CredentialProviderConfig).(interface { + MarshalToSizedBufferVT([]byte) (int, error) + }); ok { + size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarint(dAtA, i, uint64(size)) + } else { + encoded, err := proto.Marshal(m.CredentialProviderConfig) + if err != nil { + return 0, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = encodeVarint(dAtA, i, uint64(len(encoded))) + } + i-- + dAtA[i] = 0x6a + } if m.EnableFsQuotaMonitoring { i-- if m.EnableFsQuotaMonitoring { @@ -1333,6 +1355,28 @@ func (m *KubeletSpecSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if m.CredentialProviderConfig != nil { + if vtmsg, ok := interface{}(m.CredentialProviderConfig).(interface { + MarshalToSizedBufferVT([]byte) (int, error) + }); ok { + size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarint(dAtA, i, uint64(size)) + } else { + encoded, err := proto.Marshal(m.CredentialProviderConfig) + if err != nil { + return 0, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = encodeVarint(dAtA, i, uint64(len(encoded))) + } + i-- + dAtA[i] = 0x32 + } if m.Config != nil { if vtmsg, ok := interface{}(m.Config).(interface { MarshalToSizedBufferVT([]byte) (int, error) @@ -2817,6 +2861,16 @@ func (m *KubeletConfigSpec) SizeVT() (n int) { if m.EnableFsQuotaMonitoring { n += 2 } + if m.CredentialProviderConfig != nil { + if size, ok := interface{}(m.CredentialProviderConfig).(interface { + SizeVT() int + }); ok { + l = size.SizeVT() + } else { + l = proto.Size(m.CredentialProviderConfig) + } + n += 1 + l + sov(uint64(l)) + } n += len(m.unknownFields) return n } @@ -2863,6 +2917,16 @@ func (m *KubeletSpecSpec) SizeVT() (n int) { } n += 1 + l + sov(uint64(l)) } + if m.CredentialProviderConfig != nil { + if size, ok := interface{}(m.CredentialProviderConfig).(interface { + SizeVT() int + }); ok { + l = size.SizeVT() + } else { + l = proto.Size(m.CredentialProviderConfig) + } + n += 1 + l + sov(uint64(l)) + } n += len(m.unknownFields) return n } @@ -6763,6 +6827,50 @@ func (m *KubeletConfigSpec) UnmarshalVT(dAtA []byte) error { } } m.EnableFsQuotaMonitoring = bool(v != 0) + case 13: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CredentialProviderConfig", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.CredentialProviderConfig == nil { + m.CredentialProviderConfig = &structpb.Struct{} + } + if unmarshal, ok := interface{}(m.CredentialProviderConfig).(interface { + UnmarshalVT([]byte) error + }); ok { + if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + } else { + if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.CredentialProviderConfig); err != nil { + return err + } + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skip(dAtA[iNdEx:]) @@ -6996,6 +7104,50 @@ func (m *KubeletSpecSpec) UnmarshalVT(dAtA []byte) error { } } iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CredentialProviderConfig", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.CredentialProviderConfig == nil { + m.CredentialProviderConfig = &structpb.Struct{} + } + if unmarshal, ok := interface{}(m.CredentialProviderConfig).(interface { + UnmarshalVT([]byte) error + }); ok { + if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + } else { + if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.CredentialProviderConfig); err != nil { + return err + } + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skip(dAtA[iNdEx:]) diff --git a/pkg/machinery/config/config/machine.go b/pkg/machinery/config/config/machine.go index 7262a42d28..d785264ad6 100644 --- a/pkg/machinery/config/config/machine.go +++ b/pkg/machinery/config/config/machine.go @@ -298,12 +298,15 @@ type Time interface { // Kubelet defines the requirements for a config that pertains to kubelet // related options. +// +//nolint:interfacebloat type Kubelet interface { Image() string ClusterDNS() []string ExtraArgs() map[string]string ExtraMounts() []specs.Mount ExtraConfig() map[string]interface{} + CredentialProviderConfig() map[string]interface{} DefaultRuntimeSeccompProfileEnabled() bool RegisterWithFQDN() bool NodeIP() KubeletNodeIP diff --git a/pkg/machinery/config/types/v1alpha1/schemas/v1alpha1_config.schema.json b/pkg/machinery/config/types/v1alpha1/schemas/v1alpha1_config.schema.json index 4b3b35b849..ea0b308088 100644 --- a/pkg/machinery/config/types/v1alpha1/schemas/v1alpha1_config.schema.json +++ b/pkg/machinery/config/types/v1alpha1/schemas/v1alpha1_config.schema.json @@ -1735,6 +1735,13 @@ "markdownDescription": "The `extraConfig` field is used to provide kubelet configuration overrides.\n\nSome fields are not allowed to be overridden: authentication and authorization, cgroups\nconfiguration, ports, etc.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eextraConfig\u003c/code\u003e field is used to provide kubelet configuration overrides.\u003c/p\u003e\n\n\u003cp\u003eSome fields are not allowed to be overridden: authentication and authorization, cgroups\nconfiguration, ports, etc.\u003c/p\u003e\n" }, + "credentialProviderConfig": { + "type": "object", + "title": "credentialProviderConfig", + "description": "The KubeletCredentialProviderConfig field is used to provide kubelet credential configuration.\n", + "markdownDescription": "The `KubeletCredentialProviderConfig` field is used to provide kubelet credential configuration.", + "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eKubeletCredentialProviderConfig\u003c/code\u003e field is used to provide kubelet credential configuration.\u003c/p\u003e\n" + }, "defaultRuntimeSeccompProfileEnabled": { "type": "boolean", "title": "defaultRuntimeSeccompProfileEnabled", diff --git a/pkg/machinery/config/types/v1alpha1/v1alpha1_examples.go b/pkg/machinery/config/types/v1alpha1/v1alpha1_examples.go index 28a03adf2a..f157b5637b 100644 --- a/pkg/machinery/config/types/v1alpha1/v1alpha1_examples.go +++ b/pkg/machinery/config/types/v1alpha1/v1alpha1_examples.go @@ -639,6 +639,29 @@ func kubeletExtraConfigExample() Unstructured { } } +func kubeletCredentialProviderConfigExample() Unstructured { + return Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "kubelet.config.k8s.io/v1", + "kind": "CredentialProviderConfig", + "providers": []interface{}{ + map[string]interface{}{ + "name": "ecr-credential-provider", + "apiVersion": "credentialprovider.kubelet.k8s.io/v1", + "matchImages": []interface{}{ + "*.dkr.ecr.*.amazonaws.com", + "*.dkr.ecr.*.amazonaws.com.cn", + "*.dkr.ecr-fips.*.amazonaws.com", + "*.dkr.ecr.us-iso-east-1.c2s.ic.gov", + "*.dkr.ecr.us-isob-east-1.sc2s.sgov.gov", + }, + "defaultCacheDuration": "12h", + }, + }, + }, + } +} + func loggingEndpointExample1() *Endpoint { return &Endpoint{ mustParseURL("udp://127.0.0.1:12345"), diff --git a/pkg/machinery/config/types/v1alpha1/v1alpha1_provider.go b/pkg/machinery/config/types/v1alpha1/v1alpha1_provider.go index b8c1180a6c..4e8c26b464 100644 --- a/pkg/machinery/config/types/v1alpha1/v1alpha1_provider.go +++ b/pkg/machinery/config/types/v1alpha1/v1alpha1_provider.go @@ -417,6 +417,11 @@ func (k *KubeletConfig) ExtraConfig() map[string]interface{} { return k.KubeletExtraConfig.Object } +// CredentialProviderConfig implements the config.Provider interface. +func (k *KubeletConfig) CredentialProviderConfig() map[string]interface{} { + return k.KubeletCredentialProviderConfig.Object +} + // DefaultRuntimeSeccompProfileEnabled implements the config.Provider interface. func (k *KubeletConfig) DefaultRuntimeSeccompProfileEnabled() bool { return pointer.SafeDeref(k.KubeletDefaultRuntimeSeccompProfileEnabled) diff --git a/pkg/machinery/config/types/v1alpha1/v1alpha1_types.go b/pkg/machinery/config/types/v1alpha1/v1alpha1_types.go index c0a0863263..cbb16b5c85 100644 --- a/pkg/machinery/config/types/v1alpha1/v1alpha1_types.go +++ b/pkg/machinery/config/types/v1alpha1/v1alpha1_types.go @@ -592,6 +592,13 @@ type KubeletConfig struct { // type: object KubeletExtraConfig Unstructured `yaml:"extraConfig,omitempty"` // description: | + // The `KubeletCredentialProviderConfig` field is used to provide kubelet credential configuration. + // examples: + // - value: kubeletCredentialProviderConfigExample() + // schema: + // type: object + KubeletCredentialProviderConfig Unstructured `yaml:"credentialProviderConfig,omitempty"` + // description: | // Enable container runtime default Seccomp profile. // values: // - true diff --git a/pkg/machinery/config/types/v1alpha1/v1alpha1_types_doc.go b/pkg/machinery/config/types/v1alpha1/v1alpha1_types_doc.go index b481c7d886..1bf7cbca81 100644 --- a/pkg/machinery/config/types/v1alpha1/v1alpha1_types_doc.go +++ b/pkg/machinery/config/types/v1alpha1/v1alpha1_types_doc.go @@ -781,6 +781,13 @@ func (KubeletConfig) Doc() *encoder.Doc { Description: "The `extraConfig` field is used to provide kubelet configuration overrides.\n\nSome fields are not allowed to be overridden: authentication and authorization, cgroups\nconfiguration, ports, etc.", Comments: [3]string{"" /* encoder.HeadComment */, "The `extraConfig` field is used to provide kubelet configuration overrides." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, + { + Name: "credentialProviderConfig", + Type: "Unstructured", + Note: "", + Description: "The `KubeletCredentialProviderConfig` field is used to provide kubelet credential configuration.", + Comments: [3]string{"" /* encoder.HeadComment */, "The `KubeletCredentialProviderConfig` field is used to provide kubelet credential configuration." /* encoder.LineComment */, "" /* encoder.FootComment */}, + }, { Name: "defaultRuntimeSeccompProfileEnabled", Type: "bool", @@ -852,7 +859,8 @@ func (KubeletConfig) Doc() *encoder.Doc { }) doc.Fields[3].AddExample("", kubeletExtraMountsExample()) doc.Fields[4].AddExample("", kubeletExtraConfigExample()) - doc.Fields[7].AddExample("", kubeletNodeIPExample()) + doc.Fields[5].AddExample("", kubeletCredentialProviderConfigExample()) + doc.Fields[8].AddExample("", kubeletNodeIPExample()) return doc } diff --git a/pkg/machinery/config/types/v1alpha1/zz_generated.deepcopy.go b/pkg/machinery/config/types/v1alpha1/zz_generated.deepcopy.go index bada9e8947..8fd826d646 100644 --- a/pkg/machinery/config/types/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/machinery/config/types/v1alpha1/zz_generated.deepcopy.go @@ -1319,6 +1319,7 @@ func (in *KubeletConfig) DeepCopyInto(out *KubeletConfig) { } } in.KubeletExtraConfig.DeepCopyInto(&out.KubeletExtraConfig) + in.KubeletCredentialProviderConfig.DeepCopyInto(&out.KubeletCredentialProviderConfig) if in.KubeletDefaultRuntimeSeccompProfileEnabled != nil { in, out := &in.KubeletDefaultRuntimeSeccompProfileEnabled, &out.KubeletDefaultRuntimeSeccompProfileEnabled *out = new(bool) diff --git a/pkg/machinery/constants/constants.go b/pkg/machinery/constants/constants.go index 0556a66c05..cc59f2819e 100644 --- a/pkg/machinery/constants/constants.go +++ b/pkg/machinery/constants/constants.go @@ -186,8 +186,11 @@ const ( // KubernetesDefaultCertificateValidityDuration specifies default certificate duration for Kubernetes generated certificates. KubernetesDefaultCertificateValidityDuration = time.Hour * 24 * 365 + // KubernetesConfigBaseDir is the path to the base Kubernetes configuration directory. + KubernetesConfigBaseDir = "/etc/kubernetes" + // DefaultCertificatesDir is the path the Kubernetes PKI directory. - DefaultCertificatesDir = "/etc/kubernetes/pki" + DefaultCertificatesDir = KubernetesConfigBaseDir + "/" + "pki" // KubernetesCACert is the path to the root CA certificate. KubernetesCACert = DefaultCertificatesDir + "/" + "ca.crt" @@ -284,7 +287,13 @@ const ( // KubeletBootstrapKubeconfig is the path to the kubeconfig required to // bootstrap the kubelet. - KubeletBootstrapKubeconfig = "/etc/kubernetes/bootstrap-kubeconfig" + KubeletBootstrapKubeconfig = KubernetesConfigBaseDir + "/" + "bootstrap-kubeconfig" + + // KubeletCredentialProviderBinDir is the path to the directory where kubelet credential provider binaries are stored. + KubeletCredentialProviderBinDir = "/usr/local/lib/kubelet/credentialproviders" + + // KubeletCredentialProviderConfig is the path to the kubelet credential provider config. + KubeletCredentialProviderConfig = KubernetesConfigBaseDir + "/" + "kubelet-credentialproviderconfig.yaml" // KubeletPort is the kubelet port for secure API. KubeletPort = 10250 @@ -345,13 +354,13 @@ const ( LabelNodeRoleControlPlane = "node-role.kubernetes.io/control-plane" // ManifestsDirectory is the directory that contains all static manifests. - ManifestsDirectory = "/etc/kubernetes/manifests" + ManifestsDirectory = KubernetesConfigBaseDir + "/" + "manifests" // TalosManifestPrefix is the prefix for static pod files created in ManifestsDirectory by Talos. TalosManifestPrefix = "talos-" // KubeletKubeconfig is the generated kubeconfig for kubelet. - KubeletKubeconfig = "/etc/kubernetes/kubeconfig-kubelet" + KubeletKubeconfig = KubernetesConfigBaseDir + "/" + "kubeconfig-kubelet" // KubeletSystemReservedCPU cpu system reservation value for kubelet kubeconfig. KubeletSystemReservedCPU = "50m" @@ -421,13 +430,13 @@ const ( VMwareGuestInfoOvfEnvKey = "ovfenv" // AuditPolicyPath is the path to the audit-policy.yaml relative to initramfs. - AuditPolicyPath = "/etc/kubernetes/audit-policy.yaml" + AuditPolicyPath = KubernetesConfigBaseDir + "/" + "audit-policy.yaml" // EncryptionConfigPath is the path to the EncryptionConfig relative to initramfs. - EncryptionConfigPath = "/etc/kubernetes/encryptionconfig.yaml" + EncryptionConfigPath = KubernetesConfigBaseDir + "/" + "encryptionconfig.yaml" // EncryptionConfigRootfsPath is the path to the EncryptionConfig relative to rootfs. - EncryptionConfigRootfsPath = "/etc/kubernetes/encryptionconfig.yaml" + EncryptionConfigRootfsPath = KubernetesConfigBaseDir + "/" + "encryptionconfig.yaml" // ApidPort is the port for the apid service. ApidPort = 50000 @@ -956,7 +965,7 @@ const ( // Overlays is the set of paths to create overlay mounts for. var Overlays = []string{ "/etc/cni", - "/etc/kubernetes", + KubernetesConfigBaseDir, "/usr/libexec/kubernetes", "/opt", } diff --git a/pkg/machinery/resources/k8s/deep_copy.generated.go b/pkg/machinery/resources/k8s/deep_copy.generated.go index 1347e06735..77f4aae459 100644 --- a/pkg/machinery/resources/k8s/deep_copy.generated.go +++ b/pkg/machinery/resources/k8s/deep_copy.generated.go @@ -237,6 +237,12 @@ func (o KubeletSpecSpec) DeepCopy() KubeletSpecSpec { cp.Config[k2] = v2 } } + if o.CredentialProviderConfig != nil { + cp.CredentialProviderConfig = make(map[string]any, len(o.CredentialProviderConfig)) + for k2, v2 := range o.CredentialProviderConfig { + cp.CredentialProviderConfig[k2] = v2 + } + } return cp } @@ -323,6 +329,12 @@ func (o KubeletConfigSpec) DeepCopy() KubeletConfigSpec { cp.ExtraConfig[k2] = v2 } } + if o.CredentialProviderConfig != nil { + cp.CredentialProviderConfig = make(map[string]any, len(o.CredentialProviderConfig)) + for k2, v2 := range o.CredentialProviderConfig { + cp.CredentialProviderConfig[k2] = v2 + } + } return cp } diff --git a/pkg/machinery/resources/k8s/kubelet_config.go b/pkg/machinery/resources/k8s/kubelet_config.go index 2327ec2f31..59ee8bda36 100644 --- a/pkg/machinery/resources/k8s/kubelet_config.go +++ b/pkg/machinery/resources/k8s/kubelet_config.go @@ -39,6 +39,7 @@ type KubeletConfigSpec struct { StaticPodListURL string `yaml:"staticPodListURL" protobuf:"10"` DisableManifestsDirectory bool `yaml:"disableManifestsDirectory" protobuf:"11"` EnableFSQuotaMonitoring bool `yaml:"enableFSQuotaMonitoring" protobuf:"12"` + CredentialProviderConfig map[string]any `yaml:"credentialProviderConfig,omitempty" protobuf:"13"` } // NewKubeletConfig initializes an empty KubeletConfig resource. diff --git a/pkg/machinery/resources/k8s/kubelet_spec.go b/pkg/machinery/resources/k8s/kubelet_spec.go index ba29b7022e..8786fd66b1 100644 --- a/pkg/machinery/resources/k8s/kubelet_spec.go +++ b/pkg/machinery/resources/k8s/kubelet_spec.go @@ -24,11 +24,12 @@ type KubeletSpec = typed.Resource[KubeletSpecSpec, KubeletSpecExtension] // //gotagsrewrite:gen type KubeletSpecSpec struct { - Image string `yaml:"image" protobuf:"1"` - Args []string `yaml:"args,omitempty" protobuf:"2"` - ExtraMounts []specs.Mount `yaml:"extraMounts,omitempty" protobuf:"3"` - ExpectedNodename string `yaml:"expectedNodename,omitempty" protobuf:"4"` - Config map[string]any `yaml:"config" protobuf:"5"` + Image string `yaml:"image" protobuf:"1"` + Args []string `yaml:"args,omitempty" protobuf:"2"` + ExtraMounts []specs.Mount `yaml:"extraMounts,omitempty" protobuf:"3"` + ExpectedNodename string `yaml:"expectedNodename,omitempty" protobuf:"4"` + Config map[string]any `yaml:"config" protobuf:"5"` + CredentialProviderConfig map[string]any `yaml:"credentialProviderConfig,omitempty" protobuf:"6"` } // NewKubeletSpec initializes an empty KubeletSpec resource. diff --git a/website/content/v1.6/reference/api.md b/website/content/v1.6/reference/api.md index 573cb72713..68ed07e936 100644 --- a/website/content/v1.6/reference/api.md +++ b/website/content/v1.6/reference/api.md @@ -2079,6 +2079,7 @@ KubeletConfigSpec holds the source of kubelet configuration. | static_pod_list_url | [string](#string) | | | | disable_manifests_directory | [bool](#bool) | | | | enable_fs_quota_monitoring | [bool](#bool) | | | +| credential_provider_config | [google.protobuf.Struct](#google.protobuf.Struct) | | | @@ -2114,6 +2115,7 @@ KubeletSpecSpec holds the source of kubelet configuration. | extra_mounts | [talos.resource.definitions.proto.Mount](#talos.resource.definitions.proto.Mount) | repeated | | | expected_nodename | [string](#string) | | | | config | [google.protobuf.Struct](#google.protobuf.Struct) | | | +| credential_provider_config | [google.protobuf.Struct](#google.protobuf.Struct) | | | diff --git a/website/content/v1.6/reference/configuration.md b/website/content/v1.6/reference/configuration.md index d10afebbe4..fc11684d3c 100644 --- a/website/content/v1.6/reference/configuration.md +++ b/website/content/v1.6/reference/configuration.md @@ -126,6 +126,21 @@ kubelet: # extraConfig: # serverTLSBootstrap: true + # # The `KubeletCredentialProviderConfig` field is used to provide kubelet credential configuration. + # credentialProviderConfig: + # apiVersion: kubelet.config.k8s.io/v1 + # kind: CredentialProviderConfig + # providers: + # - apiVersion: credentialprovider.kubelet.k8s.io/v1 + # defaultCacheDuration: 12h + # matchImages: + # - '*.dkr.ecr.*.amazonaws.com' + # - '*.dkr.ecr.*.amazonaws.com.cn' + # - '*.dkr.ecr-fips.*.amazonaws.com' + # - '*.dkr.ecr.us-iso-east-1.c2s.ic.gov' + # - '*.dkr.ecr.us-isob-east-1.sc2s.sgov.gov' + # name: ecr-credential-provider + # # The `nodeIP` field is used to configure `--node-ip` flag for the kubelet. # nodeIP: # # The `validSubnets` field configures the networks to pick kubelet node IP from. @@ -804,6 +819,21 @@ extraArgs: # extraConfig: # serverTLSBootstrap: true +# # The `KubeletCredentialProviderConfig` field is used to provide kubelet credential configuration. +# credentialProviderConfig: +# apiVersion: kubelet.config.k8s.io/v1 +# kind: CredentialProviderConfig +# providers: +# - apiVersion: credentialprovider.kubelet.k8s.io/v1 +# defaultCacheDuration: 12h +# matchImages: +# - '*.dkr.ecr.*.amazonaws.com' +# - '*.dkr.ecr.*.amazonaws.com.cn' +# - '*.dkr.ecr-fips.*.amazonaws.com' +# - '*.dkr.ecr.us-iso-east-1.c2s.ic.gov' +# - '*.dkr.ecr.us-isob-east-1.sc2s.sgov.gov' +# name: ecr-credential-provider + # # The `nodeIP` field is used to configure `--node-ip` flag for the kubelet. # nodeIP: # # The `validSubnets` field configures the networks to pick kubelet node IP from. @@ -843,6 +873,21 @@ extraMounts: extraConfig: serverTLSBootstrap: true {{< /highlight >}} | | +|`credentialProviderConfig` |Unstructured |The `KubeletCredentialProviderConfig` field is used to provide kubelet credential configuration.
Show example(s){{< highlight yaml >}} +credentialProviderConfig: + apiVersion: kubelet.config.k8s.io/v1 + kind: CredentialProviderConfig + providers: + - apiVersion: credentialprovider.kubelet.k8s.io/v1 + defaultCacheDuration: 12h + matchImages: + - '*.dkr.ecr.*.amazonaws.com' + - '*.dkr.ecr.*.amazonaws.com.cn' + - '*.dkr.ecr-fips.*.amazonaws.com' + - '*.dkr.ecr.us-iso-east-1.c2s.ic.gov' + - '*.dkr.ecr.us-isob-east-1.sc2s.sgov.gov' + name: ecr-credential-provider +{{< /highlight >}}
| | |`defaultRuntimeSeccompProfileEnabled` |bool |Enable container runtime default Seccomp profile. |`true`
`yes`
`false`
`no`
| |`registerWithFQDN` |bool |
The `registerWithFQDN` field is used to force kubelet to use the node FQDN for registration.This is required in clouds like AWS.
|`true`
`yes`
`false`
`no`
| |`nodeIP` |KubeletNodeIPConfig |
The `nodeIP` field is used to configure `--node-ip` flag for the kubelet.This is used when a node has multiple addresses to choose from.
Show example(s){{< highlight yaml >}} diff --git a/website/content/v1.6/schemas/v1alpha1_config.schema.json b/website/content/v1.6/schemas/v1alpha1_config.schema.json index 4b3b35b849..ea0b308088 100644 --- a/website/content/v1.6/schemas/v1alpha1_config.schema.json +++ b/website/content/v1.6/schemas/v1alpha1_config.schema.json @@ -1735,6 +1735,13 @@ "markdownDescription": "The `extraConfig` field is used to provide kubelet configuration overrides.\n\nSome fields are not allowed to be overridden: authentication and authorization, cgroups\nconfiguration, ports, etc.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eextraConfig\u003c/code\u003e field is used to provide kubelet configuration overrides.\u003c/p\u003e\n\n\u003cp\u003eSome fields are not allowed to be overridden: authentication and authorization, cgroups\nconfiguration, ports, etc.\u003c/p\u003e\n" }, + "credentialProviderConfig": { + "type": "object", + "title": "credentialProviderConfig", + "description": "The KubeletCredentialProviderConfig field is used to provide kubelet credential configuration.\n", + "markdownDescription": "The `KubeletCredentialProviderConfig` field is used to provide kubelet credential configuration.", + "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eKubeletCredentialProviderConfig\u003c/code\u003e field is used to provide kubelet credential configuration.\u003c/p\u003e\n" + }, "defaultRuntimeSeccompProfileEnabled": { "type": "boolean", "title": "defaultRuntimeSeccompProfileEnabled",