diff --git a/Makefile b/Makefile index 9d18d80f..64e56515 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ # To re-generate a bundle for another specific version without changing the standard setup, you can: # - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2) # - use environment variables to overwrite this value (e.g export VERSION=0.0.2) -VERSION ?= 0.19.0 +VERSION ?= 0.21.0 # CHANNELS define the bundle channels used in the bundle. # Add a new line here if you would like to change its default config. (E.g CHANNELS = "candidate,fast,stable") @@ -50,7 +50,7 @@ endif IMG ?= controller:latest # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. -ENVTEST_K8S_VERSION = 1.32.0 +ENVTEST_K8S_VERSION = 1.35.0 # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN)) @@ -161,7 +161,7 @@ uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified .PHONY: deploy deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. cd config/manager && $(KUSTOMIZE) edit set image quay.io/confidential-containers/trustee-operator=${IMG} - $(KUSTOMIZE) build config/default | $(KUBECTL) apply -f - + $(KUSTOMIZE) build config/default | sed '/- name: OPERATOR_IMAGE_NAME/{n;s|value:.*|value: $(IMG)|;}' | $(KUBECTL) apply -f - .PHONY: undeploy undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. @@ -176,8 +176,8 @@ build-installer: manifests generate kustomize ## Generate a consolidated YAML wi # Run sample attestation in a kind cluster # pre-requirements: kuttl plugin and kind are installed # Usage: KBS_IMAGE_NAME= CLIENT_IMAGE_NAME= make test-e2e -KBS_IMAGE_NAME ?= ghcr.io/confidential-containers/staged-images/kbs:b2442c222485b6ec5d6dee09d5a30bb561ff3622 -CLIENT_IMAGE_NAME ?= quay.io/confidential-containers/kbs-client:v0.19.0 +KBS_IMAGE_NAME ?= ghcr.io/confidential-containers/key-broker-service:built-in-as-v0.21.0 +CLIENT_IMAGE_NAME ?= quay.io/confidential-containers/kbs-client:v0.21.0 .PHONY: test-e2e test-e2e: ./tests/scripts/kind-with-registry.sh @@ -193,7 +193,7 @@ $(LOCALBIN): ## Tool Versions KUSTOMIZE_VERSION ?= v5.4.3 -CONTROLLER_TOOLS_VERSION ?= v0.18.0 +CONTROLLER_TOOLS_VERSION ?= v0.21.0 ENVTEST_VERSION ?= release-0.22 GOLANGCI_LINT_VERSION ?= v2.1.0 diff --git a/README.md b/README.md index 4d01e3d4..fed89382 100644 --- a/README.md +++ b/README.md @@ -62,10 +62,6 @@ type KbsConfigSpec struct { // +optional KbsResourcePolicyConfigMapName string `json:"kbsResourcePolicyConfigMapName,omitempty"` - // TdxConfigSpec is the struct that hosts the TDX specific configuration - // +optional - TdxConfigSpec TdxConfigSpec `json:"tdxConfigSpec,omitempty"` - // IbmSEConfigSpec is the struct that hosts the IBMSE specific configuration // +optional IbmSEConfigSpec IbmSEConfigSpec `json:"ibmSEConfigSpec,omitempty"` @@ -84,13 +80,6 @@ type IbmSEConfigSpec struct { CertStorePvc string `json:"certStorePvc,omitempty"` } -// TdxConfigSpec defines the desired state for TDX configuration -type TdxConfigSpec struct { - // kbsTdxConfigMapName is the name of the configmap containing sgx_default_qcnl.conf file - // +optional - KbsTdxConfigMapName string `json:"kbsTdxConfigMapName,omitempty"` -} - // KbsLocalCertCacheSpec defines the configuration for mounting local certificates into trustee file system type KbsLocalCertCacheSpec struct { // SecretName is the name of the secret that maps to a local directory containing the certificates @@ -212,9 +201,6 @@ spec: kbsAttestationPolicyConfigMapName: attestation-policy # Resource policy kbsResourcePolicyConfigMapName: resource-policy - # TDX settings - tdxConfigSpec: - kbsTdxConfigMapName: tdx-config-sample # IBMSE settings ibmSEConfigSpec: certStorePvc: ibmse-pvc diff --git a/api/v1alpha1/kbsconfig_types.go b/api/v1alpha1/kbsconfig_types.go index b0ea3da7..7679f229 100644 --- a/api/v1alpha1/kbsconfig_types.go +++ b/api/v1alpha1/kbsconfig_types.go @@ -36,13 +36,6 @@ const ( DeploymentTypeMicroservices DeploymentType = "MicroservicesDeployment" ) -// TdxConfigSpec defines the desired state for TDX configuration -type TdxConfigSpec struct { - // kbsTdxConfigMapName is the name of the configmap containing sgx_default_qcnl.conf file - // +optional - KbsTdxConfigMapName string `json:"kbsTdxConfigMapName,omitempty"` -} - // IbmSEConfigSpec defines the desired state for IBMSE configuration type IbmSEConfigSpec struct { // certStorePvc is the name of the PeristentVolumeClaim where certificates/keys are mounted @@ -206,10 +199,6 @@ type KbsConfigSpec struct { // +optional KbsResourcePolicyConfigMapName string `json:"kbsResourcePolicyConfigMapName,omitempty"` - // TdxConfigSpec is the struct that hosts the TDX specific configuration - // +optional - TdxConfigSpec TdxConfigSpec `json:"tdxConfigSpec,omitempty"` - // IbmSEConfigSpec is the struct that hosts the IBMSE specific configuration // +optional IbmSEConfigSpec IbmSEConfigSpec `json:"ibmSEConfigSpec,omitempty"` @@ -288,6 +277,16 @@ const ( ProfileTypeRestrictive ProfileType = "Restricted" ) +// IbmSETeeConfig holds IBM Secure Execution specific configuration. +// Its presence in the spec enables IBM SE mode. +type IbmSETeeConfig struct { + // PVName is the name of the pre-existing PersistentVolume that holds the IBM SE + // certificates and keys (mounted at /opt/confidential-containers/ibmse on worker nodes). + // The PV must be created by the cluster administrator before the TrusteeConfig is applied. + // The operator creates a PVC that binds to this PV and wires it into the KbsConfig. + PVName string `json:"pvName"` +} + // TrusteeConfigSpec defines the desired state of TrusteeConfig type TrusteeConfigSpec struct { // HttpsSpec is the struct that hosts the HTTPS configuration @@ -301,6 +300,12 @@ type TrusteeConfigSpec struct { // ProfileType determines how to configure trustee, e.g. in permissive/restricted mode etc. Profile ProfileType `json:"profileType,omitempty"` + // IbmSE enables IBM Secure Execution mode when set. + // The operator will create a PVC bound to the named PV and wire it into the KbsConfig. + // CPU/GPU attestation policy ConfigMaps are skipped when this field is set. + // +optional + IbmSE *IbmSETeeConfig `json:"ibmSE,omitempty"` + // KbsServiceType is the type of service to create for KBS // Default value is ClusterIP // +optional diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 31004212..3ae0e00d 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -70,6 +70,21 @@ func (in *IbmSEConfigSpec) DeepCopy() *IbmSEConfigSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IbmSETeeConfig) DeepCopyInto(out *IbmSETeeConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IbmSETeeConfig. +func (in *IbmSETeeConfig) DeepCopy() *IbmSETeeConfig { + if in == nil { + return nil + } + out := new(IbmSETeeConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KbsConfig) DeepCopyInto(out *KbsConfig) { *out = *in @@ -137,7 +152,6 @@ func (in *KbsConfigSpec) DeepCopyInto(out *KbsConfigSpec) { *out = make([]string, len(*in)) copy(*out, *in) } - out.TdxConfigSpec = in.TdxConfigSpec out.IbmSEConfigSpec = in.IbmSEConfigSpec if in.KbsEnvVars != nil { in, out := &in.KbsEnvVars, &out.KbsEnvVars @@ -230,21 +244,6 @@ func (in *KbsLocalCertCacheSpec) DeepCopy() *KbsLocalCertCacheSpec { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TdxConfigSpec) DeepCopyInto(out *TdxConfigSpec) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TdxConfigSpec. -func (in *TdxConfigSpec) DeepCopy() *TdxConfigSpec { - if in == nil { - return nil - } - out := new(TdxConfigSpec) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TlsConfig) DeepCopyInto(out *TlsConfig) { *out = *in @@ -334,6 +333,11 @@ func (in *TrusteeConfigSpec) DeepCopyInto(out *TrusteeConfigSpec) { *out = *in out.HttpsSpec = in.HttpsSpec out.AttestationTokenVerificationSpec = in.AttestationTokenVerificationSpec + if in.IbmSE != nil { + in, out := &in.IbmSE, &out.IbmSE + *out = new(IbmSETeeConfig) + **out = **in + } if in.TlsConfig != nil { in, out := &in.TlsConfig, &out.TlsConfig *out = new(TlsConfig) diff --git a/bundle.Dockerfile b/bundle.Dockerfile index 7dc2317c..ff12331c 100644 --- a/bundle.Dockerfile +++ b/bundle.Dockerfile @@ -6,7 +6,7 @@ LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/ LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ LABEL operators.operatorframework.io.bundle.package.v1=trustee-operator LABEL operators.operatorframework.io.bundle.channels.v1=alpha -LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.42.0 +LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.42.3 LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1 LABEL operators.operatorframework.io.metrics.project_layout=go.kubebuilder.io/v4 diff --git a/bundle/manifests/confidentialcontainers.org_kbsconfigs.yaml b/bundle/manifests/confidentialcontainers.org_kbsconfigs.yaml index bcc3e05b..b9052e54 100644 --- a/bundle/manifests/confidentialcontainers.org_kbsconfigs.yaml +++ b/bundle/manifests/confidentialcontainers.org_kbsconfigs.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.21.0 creationTimestamp: null name: kbsconfigs.confidentialcontainers.org spec: @@ -163,15 +163,6 @@ spec: KbsServiceType is the type of service to create for KBS Default value is ClusterIP type: string - tdxConfigSpec: - description: TdxConfigSpec is the struct that hosts the TDX specific - configuration - properties: - kbsTdxConfigMapName: - description: kbsTdxConfigMapName is the name of the configmap - containing sgx_default_qcnl.conf file - type: string - type: object type: object status: description: KbsConfigStatus defines the observed state of KbsConfig diff --git a/bundle/manifests/confidentialcontainers.org_trusteeconfigs.yaml b/bundle/manifests/confidentialcontainers.org_trusteeconfigs.yaml index b11d2927..a3d4b90f 100644 --- a/bundle/manifests/confidentialcontainers.org_trusteeconfigs.yaml +++ b/bundle/manifests/confidentialcontainers.org_trusteeconfigs.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.21.0 creationTimestamp: null name: trusteeconfigs.confidentialcontainers.org spec: @@ -58,6 +58,22 @@ spec: that contains the TLS certificate and private key type: string type: object + ibmSE: + description: |- + IbmSE enables IBM Secure Execution mode when set. + The operator will create a PVC bound to the named PV and wire it into the KbsConfig. + CPU/GPU attestation policy ConfigMaps are skipped when this field is set. + properties: + pvName: + description: |- + PVName is the name of the pre-existing PersistentVolume that holds the IBM SE + certificates and keys (mounted at /opt/confidential-containers/ibmse on worker nodes). + The PV must be created by the cluster administrator before the TrusteeConfig is applied. + The operator creates a PVC that binds to this PV and wires it into the KbsConfig. + type: string + required: + - pvName + type: object kbsServiceType: description: |- KbsServiceType is the type of service to create for KBS diff --git a/bundle/manifests/trustee-operator.clusterserviceversion.yaml b/bundle/manifests/trustee-operator.clusterserviceversion.yaml index ac577c9f..52f377e0 100644 --- a/bundle/manifests/trustee-operator.clusterserviceversion.yaml +++ b/bundle/manifests/trustee-operator.clusterserviceversion.yaml @@ -5,8 +5,8 @@ metadata: alm-examples: '[]' capabilities: Basic Install categories: Security - containerImage: quay.io/confidential-containers/trustee-operator:v0.19.0 - createdAt: "2026-05-21T15:49:55Z" + containerImage: quay.io/confidential-containers/trustee-operator:v0.21.0 + createdAt: "2026-07-01T09:49:27Z" features.operators.openshift.io/disconnected: "true" features.operators.openshift.io/fips-compliant: "false" features.operators.openshift.io/proxy-aware: "true" @@ -15,10 +15,10 @@ metadata: features.operators.openshift.io/token-auth-azure: "false" features.operators.openshift.io/token-auth-gcp: "false" operatorframework.io/suggested-namespace: trustee-operator-system - operators.operatorframework.io/builder: operator-sdk-v1.42.0 + operators.operatorframework.io/builder: operator-sdk-v1.42.3 operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 support: Confidential Containers Community - name: trustee-operator.v0.19.0 + name: trustee-operator.v0.21.0 namespace: placeholder spec: apiservicedefinitions: {} @@ -62,6 +62,25 @@ spec: verbs: - get - update + - apiGroups: + - "" + resources: + - persistentvolumeclaims + verbs: + - create + - delete + - get + - list + - update + - watch + - apiGroups: + - "" + resources: + - persistentvolumes + verbs: + - get + - list + - watch - apiGroups: - apps resources: @@ -160,16 +179,16 @@ spec: fieldRef: fieldPath: metadata.namespace - name: OPERATOR_IMAGE_NAME - value: quay.io/confidential-containers/trustee-operator:v0.19.0 + value: quay.io/confidential-containers/trustee-operator:v0.21.0 - name: KBS_IMAGE_NAME value: ghcr.io/confidential-containers/staged-images/kbs:b2442c222485b6ec5d6dee09d5a30bb561ff3622 - name: KBS_IMAGE_NAME_MICROSERVICES - value: ghcr.io/confidential-containers/key-broker-service:v0.19.0 + value: ghcr.io/confidential-containers/key-broker-service:v0.21.0 - name: AS_IMAGE_NAME value: ghcr.io/confidential-containers/staged-images/coco-as-grpc:latest - name: RVPS_IMAGE_NAME value: ghcr.io/confidential-containers/staged-images/rvps:latest - image: quay.io/confidential-containers/trustee-operator:v0.19.0 + image: quay.io/confidential-containers/trustee-operator:v0.21.0 livenessProbe: httpGet: path: /healthz @@ -262,5 +281,5 @@ spec: provider: name: Confidential Containers Community url: https://github.com/confidential-containers - replaces: trustee-operator.v0.18.0 - version: 0.19.0 + replaces: trustee-operator.v0.19.0 + version: 0.21.0 diff --git a/bundle/metadata/annotations.yaml b/bundle/metadata/annotations.yaml index b1bd316b..c1c44b2d 100644 --- a/bundle/metadata/annotations.yaml +++ b/bundle/metadata/annotations.yaml @@ -5,7 +5,7 @@ annotations: operators.operatorframework.io.bundle.metadata.v1: metadata/ operators.operatorframework.io.bundle.package.v1: trustee-operator operators.operatorframework.io.bundle.channels.v1: alpha - operators.operatorframework.io.metrics.builder: operator-sdk-v1.42.0 + operators.operatorframework.io.metrics.builder: operator-sdk-v1.42.3 operators.operatorframework.io.metrics.mediatype.v1: metrics+v1 operators.operatorframework.io.metrics.project_layout: go.kubebuilder.io/v4 diff --git a/config/crd/bases/confidentialcontainers.org_kbsconfigs.yaml b/config/crd/bases/confidentialcontainers.org_kbsconfigs.yaml index 6503e290..47171041 100644 --- a/config/crd/bases/confidentialcontainers.org_kbsconfigs.yaml +++ b/config/crd/bases/confidentialcontainers.org_kbsconfigs.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.21.0 name: kbsconfigs.confidentialcontainers.org spec: group: confidentialcontainers.org @@ -163,15 +163,6 @@ spec: KbsServiceType is the type of service to create for KBS Default value is ClusterIP type: string - tdxConfigSpec: - description: TdxConfigSpec is the struct that hosts the TDX specific - configuration - properties: - kbsTdxConfigMapName: - description: kbsTdxConfigMapName is the name of the configmap - containing sgx_default_qcnl.conf file - type: string - type: object type: object status: description: KbsConfigStatus defines the observed state of KbsConfig diff --git a/config/crd/bases/confidentialcontainers.org_trusteeconfigs.yaml b/config/crd/bases/confidentialcontainers.org_trusteeconfigs.yaml index 9ac5fc32..f46fe978 100644 --- a/config/crd/bases/confidentialcontainers.org_trusteeconfigs.yaml +++ b/config/crd/bases/confidentialcontainers.org_trusteeconfigs.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.21.0 name: trusteeconfigs.confidentialcontainers.org spec: group: confidentialcontainers.org @@ -58,6 +58,22 @@ spec: that contains the TLS certificate and private key type: string type: object + ibmSE: + description: |- + IbmSE enables IBM Secure Execution mode when set. + The operator will create a PVC bound to the named PV and wire it into the KbsConfig. + CPU/GPU attestation policy ConfigMaps are skipped when this field is set. + properties: + pvName: + description: |- + PVName is the name of the pre-existing PersistentVolume that holds the IBM SE + certificates and keys (mounted at /opt/confidential-containers/ibmse on worker nodes). + The PV must be created by the cluster administrator before the TrusteeConfig is applied. + The operator creates a PVC that binds to this PV and wires it into the KbsConfig. + type: string + required: + - pvName + type: object kbsServiceType: description: |- KbsServiceType is the type of service to create for KBS diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index ad490085..73182997 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -5,4 +5,4 @@ kind: Kustomization images: - name: controller newName: quay.io/confidential-containers/trustee-operator - newTag: v0.19.0 + newTag: v0.21.0 diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index f50789ca..c26a3bea 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -74,7 +74,7 @@ spec: args: - --leader-elect - --health-probe-bind-address=:8081 - image: quay.io/confidential-containers/trustee-operator:v0.19.0 + image: quay.io/confidential-containers/trustee-operator:v0.21.0 name: manager # Add the following environment variables to the manager container # POD_NAMESPACE @@ -84,13 +84,13 @@ spec: fieldRef: fieldPath: metadata.namespace - name: OPERATOR_IMAGE_NAME - value: quay.io/confidential-containers/trustee-operator:v0.19.0 + value: quay.io/confidential-containers/trustee-operator:v0.21.0 - name: KBS_IMAGE_NAME # kbs image for AllInOneDeployment value: ghcr.io/confidential-containers/staged-images/kbs:b2442c222485b6ec5d6dee09d5a30bb561ff3622 # kbs image for MicroserviceDeployment - name: KBS_IMAGE_NAME_MICROSERVICES - value: ghcr.io/confidential-containers/key-broker-service:v0.19.0 + value: ghcr.io/confidential-containers/key-broker-service:v0.21.0 - name: AS_IMAGE_NAME value: ghcr.io/confidential-containers/staged-images/coco-as-grpc:latest - name: RVPS_IMAGE_NAME diff --git a/config/manifests/bases/trustee-operator.clusterserviceversion.yaml b/config/manifests/bases/trustee-operator.clusterserviceversion.yaml index 719efe26..d8779525 100644 --- a/config/manifests/bases/trustee-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/trustee-operator.clusterserviceversion.yaml @@ -5,7 +5,7 @@ metadata: alm-examples: '[]' capabilities: Basic Install categories: Security - containerImage: quay.io/confidential-containers/trustee-operator:v0.19.0 + containerImage: quay.io/confidential-containers/trustee-operator:v0.21.0 features.operators.openshift.io/disconnected: "true" features.operators.openshift.io/fips-compliant: "false" features.operators.openshift.io/proxy-aware: "true" @@ -15,7 +15,7 @@ metadata: features.operators.openshift.io/token-auth-gcp: "false" operatorframework.io/suggested-namespace: trustee-operator-system support: Confidential Containers Community - name: trustee-operator.v0.19.0 + name: trustee-operator.v0.21.0 namespace: placeholder spec: apiservicedefinitions: {} @@ -63,5 +63,5 @@ spec: provider: name: Confidential Containers Community url: https://github.com/confidential-containers - replaces: trustee-operator.v0.18.0 - version: 0.19.0 + replaces: trustee-operator.v0.19.0 + version: 0.21.0 diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index f2036ab9..66bb564c 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -25,6 +25,25 @@ rules: verbs: - get - update +- apiGroups: + - "" + resources: + - persistentvolumeclaims + verbs: + - create + - delete + - get + - list + - update + - watch +- apiGroups: + - "" + resources: + - persistentvolumes + verbs: + - get + - list + - watch - apiGroups: - apps resources: diff --git a/config/samples/all-in-one/kustomization.yaml b/config/samples/all-in-one/kustomization.yaml index c6c3553c..725d5253 100644 --- a/config/samples/all-in-one/kustomization.yaml +++ b/config/samples/all-in-one/kustomization.yaml @@ -21,7 +21,6 @@ patches: - path: patch-ref-values.yaml - path: patch-attestation-policy.yaml - path: patch-resource-policy.yaml -- path: patch-tdx-config.yaml # uncomment the following line for injecting sample resources in kbs - path: patch-kbs-resources.yaml # uncomment the following line for enabling DEBUG logs @@ -35,7 +34,6 @@ resources: - ibmse-attestation-policy.yaml - ibmse-resource-policy.yaml - resource-policy.yaml -- tdx-config.yaml - ita-kbs-config.yaml - ita-resource-policy.yaml - ibmse-pv.yaml diff --git a/config/samples/all-in-one/patch-tdx-config.yaml b/config/samples/all-in-one/patch-tdx-config.yaml deleted file mode 100644 index 37ed4ab5..00000000 --- a/config/samples/all-in-one/patch-tdx-config.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: confidentialcontainers.org/v1alpha1 -kind: KbsConfig -metadata: - name: kbsconfig-sample - namespace: trustee-operator-system -spec: - tdxConfigSpec: - kbsTdxConfigMapName: tdx-config \ No newline at end of file diff --git a/config/samples/all-in-one/tdx-config.yaml b/config/samples/all-in-one/tdx-config.yaml deleted file mode 100644 index ffd4ceb5..00000000 --- a/config/samples/all-in-one/tdx-config.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: tdx-config - namespace: trustee-operator-system -data: - sgx_default_qcnl.conf: | - { - "collateral_service": "https://api.trustedservices.intel.com/sgx/certification/v4/", - "pccs_url": "https://api.trustedservices.intel.com/sgx/certification/v4/" - // "pccs_url": "https://localhost:8081/sgx/certification/v4/", - // To accept insecure HTTPS certificate, set this option to false - // "use_secure_cert": false - } \ No newline at end of file diff --git a/config/samples/microservices/kustomization.yaml b/config/samples/microservices/kustomization.yaml index 0bbb2390..f88c41bc 100644 --- a/config/samples/microservices/kustomization.yaml +++ b/config/samples/microservices/kustomization.yaml @@ -21,7 +21,6 @@ patches: - path: patch-ref-values.yaml - path: patch-attestation-policy.yaml - path: patch-resource-policy.yaml -- path: patch-tdx-config.yaml # inject sample resources in kbs - path: patch-kbs-resources.yaml # uncomment the following line for enabling DEBUG logs @@ -37,7 +36,6 @@ resources: - ibmse-attestation-policy.yaml - ibmse-resource-policy.yaml - resource-policy.yaml -- tdx-config.yaml - ita-kbs-config.yaml - ita-resource-policy.yaml - ibmse-pv.yaml diff --git a/config/samples/microservices/patch-tdx-config.yaml b/config/samples/microservices/patch-tdx-config.yaml deleted file mode 100644 index 37ed4ab5..00000000 --- a/config/samples/microservices/patch-tdx-config.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: confidentialcontainers.org/v1alpha1 -kind: KbsConfig -metadata: - name: kbsconfig-sample - namespace: trustee-operator-system -spec: - tdxConfigSpec: - kbsTdxConfigMapName: tdx-config \ No newline at end of file diff --git a/config/samples/microservices/tdx-config.yaml b/config/samples/microservices/tdx-config.yaml deleted file mode 100644 index ffd4ceb5..00000000 --- a/config/samples/microservices/tdx-config.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: tdx-config - namespace: trustee-operator-system -data: - sgx_default_qcnl.conf: | - { - "collateral_service": "https://api.trustedservices.intel.com/sgx/certification/v4/", - "pccs_url": "https://api.trustedservices.intel.com/sgx/certification/v4/" - // "pccs_url": "https://localhost:8081/sgx/certification/v4/", - // To accept insecure HTTPS certificate, set this option to false - // "use_secure_cert": false - } \ No newline at end of file diff --git a/config/templates/ear_default_attestation_policy_cpu.rego b/config/templates/ear_default_attestation_policy_cpu.rego index 83747391..608cccc3 100644 --- a/config/templates/ear_default_attestation_policy_cpu.rego +++ b/config/templates/ear_default_attestation_policy_cpu.rego @@ -242,18 +242,22 @@ tdx_uefi_event_tdvfkernelparams_ok if { executables := 3 if { input["az-snp-vtpm"] - input["az-snp-vtpm"].measurement in query_reference_value("measurement") + # input["az-snp-vtpm"].measurement in query_reference_value("measurement") + input["az-snp-vtpm"].tpm.pcr03 in query_reference_value("snp_pcr03") + input["az-snp-vtpm"].tpm.pcr08 in query_reference_value("snp_pcr08") + input["az-snp-vtpm"].tpm.pcr09 in query_reference_value("snp_pcr09") input["az-snp-vtpm"].tpm.pcr11 in query_reference_value("snp_pcr11") + input["az-snp-vtpm"].tpm.pcr12 in query_reference_value("snp_pcr12") } hardware := 2 if { input["az-snp-vtpm"] # Check the reported TCB to validate the ASP FW - input["az-snp-vtpm"].reported_tcb_bootloader in query_reference_value("tcb_bootloader") - input["az-snp-vtpm"].reported_tcb_microcode in query_reference_value("tcb_microcode") - input["az-snp-vtpm"].reported_tcb_snp in query_reference_value("tcb_snp") - input["az-snp-vtpm"].reported_tcb_tee in query_reference_value("tcb_tee") + # input["az-snp-vtpm"].reported_tcb_bootloader in query_reference_value("tcb_bootloader") + # input["az-snp-vtpm"].reported_tcb_microcode in query_reference_value("tcb_microcode") + # input["az-snp-vtpm"].reported_tcb_snp in query_reference_value("tcb_snp") + # input["az-snp-vtpm"].reported_tcb_tee in query_reference_value("tcb_tee") } # For the 'configuration' trust claim 2 stands for @@ -263,19 +267,23 @@ hardware := 2 if { configuration := 2 if { input["az-snp-vtpm"] - input["az-snp-vtpm"].platform_smt_enabled in query_reference_value("smt_enabled") - input["az-snp-vtpm"].platform_tsme_enabled in query_reference_value("tsme_enabled") - input["az-snp-vtpm"].policy_abi_major in query_reference_value("abi_major") - input["az-snp-vtpm"].policy_abi_minor in query_reference_value("abi_minor") - input["az-snp-vtpm"].policy_single_socket in query_reference_value("single_socket") - input["az-snp-vtpm"].policy_smt_allowed in query_reference_value("smt_allowed") + # input["az-snp-vtpm"].platform_smt_enabled in query_reference_value("smt_enabled") + # input["az-snp-vtpm"].platform_tsme_enabled in query_reference_value("tsme_enabled") + # input["az-snp-vtpm"].policy_abi_major in query_reference_value("abi_major") + # input["az-snp-vtpm"].policy_abi_minor in query_reference_value("abi_minor") + # input["az-snp-vtpm"].policy_single_socket in query_reference_value("single_socket") + # input["az-snp-vtpm"].policy_smt_allowed in query_reference_value("smt_allowed") } ##### Azure vTPM TDX executables := 3 if { input["az-tdx-vtpm"] + input["az-tdx-vtpm"].tpm.pcr03 in query_reference_value("tdx_pcr03") + input["az-tdx-vtpm"].tpm.pcr08 in query_reference_value("tdx_pcr08") + input["az-tdx-vtpm"].tpm.pcr09 in query_reference_value("tdx_pcr09") input["az-tdx-vtpm"].tpm.pcr11 in query_reference_value("tdx_pcr11") + input["az-tdx-vtpm"].tpm.pcr12 in query_reference_value("tdx_pcr12") } hardware := 2 if { @@ -289,7 +297,7 @@ hardware := 2 if { # input.tdx.quote.body.mr_seam in query_reference_value("mr_seam") # # Check OVMF code hash -input["az-tdx-vtpm"].quote.body.mr_td in query_reference_value("mr_td") +# input["az-tdx-vtpm"].quote.body.mr_td in query_reference_value("mr_td") # Check TCB status (covers quote.body.tcb_svn claim check) input["az-tdx-vtpm"].tcb_status == "UpToDate" @@ -300,7 +308,7 @@ input["az-tdx-vtpm"].tcb_status == "UpToDate" configuration := 2 if { input["az-tdx-vtpm"] - input["az-tdx-vtpm"].quote.body.xfam in query_reference_value("xfam") + # input["az-tdx-vtpm"].quote.body.xfam in query_reference_value("xfam") } ##### TPM diff --git a/config/templates/kbs-config-permissive.toml b/config/templates/kbs-config-permissive.toml index c3bc9298..fe62ee8e 100644 --- a/config/templates/kbs-config-permissive.toml +++ b/config/templates/kbs-config-permissive.toml @@ -36,6 +36,9 @@ vcek_sources = [ { type = "KDS" } ] +[attestation_service.verifier_config.dcap_verifier] +collateral_service = "https://api.trustedservices.intel.com/sgx/certification/v4/" + [[plugins]] name = "resource" storage_backend_type = "kvstorage" diff --git a/config/templates/kbs-config-restricted.toml b/config/templates/kbs-config-restricted.toml index 129b9f99..6e4bb8db 100644 --- a/config/templates/kbs-config-restricted.toml +++ b/config/templates/kbs-config-restricted.toml @@ -51,6 +51,9 @@ vcek_sources = [ { type = "KDS" } ] +[attestation_service.verifier_config.dcap_verifier] +collateral_service = "https://api.trustedservices.intel.com/sgx/certification/v4/" + [attestation_service.attestation_token_broker.signer] key_path = "/etc/attestation-key/token.key" cert_path = "/etc/attestation-cert/token.crt" diff --git a/config/templates/resource-policy-ibm.rego b/config/templates/resource-policy-ibm.rego new file mode 100644 index 00000000..6f1ba9de --- /dev/null +++ b/config/templates/resource-policy-ibm.rego @@ -0,0 +1,18 @@ +package policy +import rego.v1 +# Default deny - only allow if all conditions pass +default allow := false +# Extract IBM SE claims from EAR token using correct path +se_claims := input.submods.cpu0["ear.veraison.annotated-evidence"].se +# Allow access if: +# 1. Plugin is "resource" (LocalFs plugin) +# 2. IBM SE claims exist +# 3. All IBM SE claim values match expected values +allow if { + data.plugin == "resource" + se_claims != null + se_claims.attestation_phkh == "" + se_claims.image_phkh == "" + se_claims.tag == "" + se_claims.version == 256 + } diff --git a/config/templates/tdx-config.json b/config/templates/tdx-config.json deleted file mode 100644 index 5b1cca3e..00000000 --- a/config/templates/tdx-config.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "collateral_service": "https://api.trustedservices.intel.com/sgx/certification/v4/" -} diff --git a/docs/disconnected.md b/docs/disconnected.md index 0d277acb..658bb8c8 100644 --- a/docs/disconnected.md +++ b/docs/disconnected.md @@ -1,32 +1,42 @@ -# Disconnected enviroment +# Disconnected environment A disconnected environment is a system that has no direct or continuous connection to the internet or other external networks. -In this guide, we bring an example on how to configure the trustee operator for baking a VCEK certificate into the trustee image. +In this guide, we bring an example on how to configure the trustee operator for baking VCEK certificates into the trustee image. ## Create the VCEK secret -Please refer to this [guide](https://github.com/confidential-containers/trustee/blob/main/attestation-service/docs/amd-offline-certificate-cache.md) for more deatails. +Please refer to this [guide](https://github.com/confidential-containers/trustee/blob/main/attestation-service/docs/amd-offline-certificate-cache.md) for more details. +First of all let's create a local directory containing the certificates. -First of all let's create a local directory containing the certificates (one per node): +Trustee supports two naming layouts per hardware ID. When both are present the TCB-prefixed file takes precedence: ``` -├── vcek -│ ├── -│ ├── vcek.der -│ ├── -│ ├── vcek.der +vcek/ +├── / +│ └── bl02_tee00_snp06_ucode21_vcek.der # preferred: TCB-prefixed filename +├── / +│ └── vcek.der # legacy flat layout (fallback) +├── / +│ ├── bl03_tee01_snp08_ucode15_fmc05_vcek.der # multiple VCEKs per host +│ └── bl02_tee00_snp06_ucode21_vcek.der ``` +The TCB prefix format is `bl{BL}_tee{TEE}_snp{SNP}_ucode{UCODE}` with each parameter zero-padded to 2 digits. +Turin processors append an additional `_fmc{FMC}` field. +Using TCB-prefixed filenames is recommended as it allows pre-loading certificates for multiple firmware versions per host. + **Note** The hardware-id must be lowercase. -Then we create a secret (one per node): +Then we create a secret (one per hardware ID): ```bash kubectl create secret generic vcek-secret1 --from-file ./vcek/ -n trustee-operator-system kubectl create secret generic vcek-secret2 --from-file ./vcek/ -n trustee-operator-system ``` +All files in the hardware ID directory (whether `vcek.der` or TCB-prefixed) are included in the secret automatically. + ## KbsConfig The KbsConfig CR needs to specify the `kbsLocalCertCacheSpec` option: @@ -48,5 +58,5 @@ spec: mountPath: "/opt/confidential-containers/attestation-service/kds-store/vcek/" ``` -The `vcek.der` certificate will be mounted in the trustee `mountPath` directory. +The VCEK certificates are mounted in the trustee `mountPath` directory. The `mountPath` directory defaults to `/opt/confidential-containers/attestation-service/kds-store/vcek` if not provided by the user. diff --git a/docs/ibmse.md b/docs/ibmse.md index 024c2e02..1c409884 100644 --- a/docs/ibmse.md +++ b/docs/ibmse.md @@ -24,21 +24,32 @@ By the end of the aforementioned procedure, you should end up having a directory └── encrypt_key.pub ``` -## Persistent Volume creation +Place this directory at `/opt/confidential-containers/ibmse` on every worker node that will run the trustee pod, and ensure the correct permissions are set: -For mounting the above directory to the trustee pod filesystem, we'd need to create a Persistent Volume (PV) and a Persistent Volume Claim (PVC). -The configuration of PV/PVC is deployment specific (e.g. dependent on cloud provider), so it is not reported here in this guide. +```bash +sudo chmod -R 755 /opt/confidential-containers/ibmse/ +``` + +--- + +## Configuring IBM SE via TrusteeConfig (recommended) + +The `TrusteeConfig` CR provides the simplest way to deploy trustee for IBM SE. When `ibmSE` is set, the operator: + +- Creates a `PersistentVolumeClaim` named `-ibmse-certstore-pvc`, bound to the PV specified in `ibmSE.pvName`, and wires it into the generated `KbsConfig` +- Skips CPU/GPU attestation policy ConfigMaps, which are not applicable to IBM SE + +> **Note:** The `PersistentVolume` is **not** created or deleted by the operator. It must be pre-created by the cluster administrator before applying the `TrusteeConfig` (see Step 1 below). `PersistentVolume` is a cluster-scoped Kubernetes resource and cannot be owned by a namespace-scoped CR. -In a development environment, you may want to create a PV/PVC that makes use of a local directory. This approach is not recommended for production environments: +### Step 1 – Create the PersistentVolume -PersistentVolume: +Apply the following manifest once per cluster. You can use the sample at `config/samples/all-in-one/ibmse-pv.yaml` as a starting point. ```yaml apiVersion: v1 kind: PersistentVolume metadata: name: ibmse-pv - namespace: trustee-operator-system spec: capacity: storage: 100Mi @@ -54,100 +65,107 @@ spec: - key: node-role.kubernetes.io/worker operator: Exists ``` -**Note:** the `path` has to match a local directory on the worker node, and the correct permission for this directory must be set: -```bash -sudo chmod -R 755 /opt/confidential-containers/ibmse/ -``` -PersistentVolumeClaim: +### Step 2 – Apply the TrusteeConfig + +Set `ibmSE.pvName` to the name of the PV created in Step 1. The operator will create a PVC named `trusteeconfig-ibmse-ibmse-certstore-pvc` that binds to it. ```yaml -apiVersion: v1 -kind: PersistentVolumeClaim +apiVersion: confidentialcontainers.org/v1alpha1 +kind: TrusteeConfig metadata: - name: ibmse-pvc + name: trusteeconfig-ibmse namespace: trustee-operator-system spec: - accessModes: - - ReadOnlyMany - storageClassName: "" - resources: - requests: - storage: 100Mi + ibmSE: + pvName: ibmse-pv + profileType: Restricted + httpsSpec: + tlsSecretName: kbs-https-certificate + kbsServiceType: NodePort ``` -## KBS with ibmse specific configuration -- Please update the `ibmse-attestation-policy` configmap with correct values +### Step 3 – Update the IBM SE policy ConfigMap + +Update the resource policy ConfigMap by following the sample at `config/templates/resource-policy-ibm.rego`. + +> **Note:** Replace ``, ``, and `` with the values for your workload. Refer to [Retrieve-the-attestation-policy-fields-for-ibm-se](https://github.com/confidential-containers/trustee/blob/main/deps/verifier/src/se/README.md#retrive-the-attestation-policy-fields-for-ibm-se) for details. + +--- + +## Configuring IBM SE via KbsConfig (advanced) + +If you manage the `KbsConfig` resource directly (without `TrusteeConfig`), you must create the PV, PVC, and all ConfigMaps manually. + +### Persistent Volume + +Create a `PersistentVolume` backed by the local IBM SE certificate directory (cluster-scoped, no namespace): + ```yaml apiVersion: v1 -kind: ConfigMap +kind: PersistentVolume metadata: - name: ibmse-attestation-policy - namespace: trustee-operator-system -data: - default.rego: | - package policy - import rego.v1 - default allow = false - converted_version := sprintf("%v", [input["se.version"]]) - - allow if { - input["se.attestation_phkh"] == "" - input["se.image_phkh"] == "" - input["se.tag"] == "" - input["se.user_data"] == "00" - converted_version == "256" - } + name: ibmse-pv +spec: + capacity: + storage: 100Mi + accessModes: + - ReadOnlyMany + storageClassName: "" + local: + path: /opt/confidential-containers/ibmse + nodeAffinity: + required: + nodeSelectorTerms: + - matchExpressions: + - key: node-role.kubernetes.io/worker + operator: Exists ``` -**Note:** Retrieve the IBM SE fields ``, `` and `` for attestation policy from [here](https://github.com/confidential-containers/trustee/blob/main/deps/verifier/src/se/README.md#set-attestation-policy) -- Please check the `ibmse-resource-policy` configmap +### PersistentVolumeClaim + +Create the PVC in the same namespace as the `KbsConfig`. Setting `volumeName` ensures static binding to the PV above. + ```yaml apiVersion: v1 -kind: ConfigMap +kind: PersistentVolumeClaim metadata: - name: ibmse-resource-policy + name: ibmse-pvc namespace: trustee-operator-system -data: - policy.rego: | - package policy - default allow = false - path := split(data["resource-path"], "/") - - allow { - count(path) == 3 - input["tee"] == "se" - } +spec: + accessModes: + - ReadOnlyMany + storageClassName: "" + volumeName: ibmse-pv + resources: + requests: + storage: 100Mi ``` -## KBS config CRD - -For enabling IBM specific configuration in trustee pod, the `KbsConfig` custom resource should have the `ibmSEConfigSpec` section populated as in the following example: +### KbsConfig CR ```yaml apiVersion: confidentialcontainers.org/v1alpha1 kind: KbsConfig -metadata: +metadata: name: kbsconfig-sample namespace: trustee-operator-system spec: # omitted all the rest of config # ... - kbsAttestationPolicyConfigMapName: ibmse-attestation-policy kbsResourcePolicyConfigMapName: ibmse-resource-policy kbsServiceType: NodePort # IBMSE settings ibmSEConfigSpec: certStorePvc: ibmse-pvc ``` -**Note:** - -- The `kbsAttestationPolicyConfigMapName` has to use `ibmse-attestation-policy` instead of default `attestation-policy`. -- The `kbsResourcePolicyConfigMapName` has to use `ibmse-resource-policy` instead of default `resource-policy`. -- The `certStorePvc` has to match the aforementioned PVC name. -- if the https is enabled, please make sure include the worker node ips to the `[alt_names]` section, here is the document about how to [generate a self signed certificate](https://github.com/confidential-containers/trustee/blob/main/kbs/docs/self-signed-https.md#generate-a-self-signed-certificate) - ```yaml - ... + +**Notes:** + +- `kbsResourcePolicyConfigMapName` must reference a ConfigMap whose `policy.rego` follows the `config/templates/resource-policy-ibm.rego` template. +- `certStorePvc` must match the PVC name created above. +- If HTTPS is enabled, include the worker node IPs in the `[alt_names]` section of your certificate. See [Generate a self-signed certificate](https://github.com/confidential-containers/trustee/blob/main/kbs/docs/self-signed-https.md#generate-a-self-signed-certificate) for details. + ```ini [alt_names] DNS.1 = kbs-service IP.1 = diff --git a/docs/kbs-config-merge-strategy.md b/docs/kbs-config-merge-strategy.md index 75fee59d..729cd775 100644 --- a/docs/kbs-config-merge-strategy.md +++ b/docs/kbs-config-merge-strategy.md @@ -18,7 +18,6 @@ These fields are **always managed** by TrusteeConfig and will be overwritten: - `KbsDeploymentType` - Deployment type (always set to AllInOneDeployment) - `KbsResourcePolicyConfigMapName` - Resource policy - `KbsAttestationPolicyConfigMapName` - Attestation policy (generated based on profile type) -- `TdxConfigSpec.KbsTdxConfigMapName` - TDX configuration - `KbsHttpsKeySecretName` - HTTPS key secret (generated when `HttpsSpec.TlsSecretName` is set) - `KbsHttpsCertSecretName` - HTTPS certificate secret (generated when `HttpsSpec.TlsSecretName` is set) diff --git a/go.mod b/go.mod index e9e81874..38d0c2d8 100644 --- a/go.mod +++ b/go.mod @@ -6,17 +6,18 @@ toolchain go1.25.9 require ( github.com/go-logr/logr v1.4.3 - github.com/onsi/ginkgo/v2 v2.22.0 - github.com/onsi/gomega v1.36.1 + github.com/onsi/ginkgo/v2 v2.27.2 + github.com/onsi/gomega v1.38.2 github.com/openshift/api v0.0.0-20251020095937-6a0c921fc0f5 - k8s.io/api v0.34.1 - k8s.io/apimachinery v0.34.1 - k8s.io/client-go v0.34.1 - sigs.k8s.io/controller-runtime v0.21.0 + k8s.io/api v0.35.0 + k8s.io/apimachinery v0.35.0 + k8s.io/client-go v0.35.0 + sigs.k8s.io/controller-runtime v0.23.3 ) require ( cel.dev/expr v0.25.1 // indirect + github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect @@ -34,12 +35,11 @@ require ( github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect - github.com/gogo/protobuf v1.3.2 // indirect github.com/google/btree v1.1.3 // indirect github.com/google/cel-go v0.26.0 // indirect github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-cmp v0.7.0 // indirect - github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect + github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -49,14 +49,13 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.22.0 // indirect - github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.62.0 // indirect - github.com/prometheus/procfs v0.15.1 // indirect - github.com/spf13/cobra v1.9.1 // indirect - github.com/spf13/pflag v1.0.6 // indirect + github.com/prometheus/client_golang v1.23.2 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.66.1 // indirect + github.com/prometheus/procfs v0.16.1 // indirect + github.com/spf13/cobra v1.10.0 // indirect + github.com/spf13/pflag v1.0.9 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect @@ -70,34 +69,35 @@ require ( go.opentelemetry.io/proto/otlp v1.10.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - go.yaml.in/yaml/v2 v2.4.2 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect - golang.org/x/net v0.52.0 // indirect + golang.org/x/mod v0.37.0 // indirect + golang.org/x/net v0.56.0 // indirect golang.org/x/oauth2 v0.35.0 // indirect - golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.42.0 // indirect - golang.org/x/term v0.41.0 // indirect - golang.org/x/text v0.35.0 // indirect + golang.org/x/sync v0.21.0 // indirect + golang.org/x/sys v0.46.0 // indirect + golang.org/x/term v0.44.0 // indirect + golang.org/x/text v0.39.0 // indirect golang.org/x/time v0.9.0 // indirect - golang.org/x/tools v0.42.0 // indirect + golang.org/x/tools v0.47.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect google.golang.org/grpc v1.80.0 // indirect google.golang.org/protobuf v1.36.11 // indirect - gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect + gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.34.0 // indirect - k8s.io/apiserver v0.34.0 // indirect - k8s.io/component-base v0.34.0 // indirect + k8s.io/apiextensions-apiserver v0.35.0 // indirect + k8s.io/apiserver v0.35.0 // indirect + k8s.io/component-base v0.35.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect - k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect + k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect + k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect - sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect + sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/go.sum b/go.sum index d92cb4f4..b08fa1e6 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -28,6 +30,12 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs= +github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= +github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= +github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk= +github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01KS3zGE= +github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -45,8 +53,8 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= +github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= @@ -60,8 +68,8 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= -github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= @@ -70,10 +78,10 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= +github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -87,6 +95,10 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= +github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= +github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= +github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -95,10 +107,10 @@ github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFd github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= -github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= -github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= -github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= +github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= +github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= +github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= github.com/openshift/api v0.0.0-20251020095937-6a0c921fc0f5 h1:P3XSHKoFPx/vW/hzN1q7l7i8mRCX/vP+4g5AdLeaNOQ= github.com/openshift/api v0.0.0-20251020095937-6a0c921fc0f5/go.mod h1:d5uzF0YN2nQQFA0jIEWzzOZ+edmo6wzlGLvx5Fhz4uY= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -106,21 +118,22 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= -github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= -github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= +github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= -github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= -github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.10.0 h1:a5/WeUlSDCvV5a45ljW2ZFtV0bTDpkfSAj3uqB6Sc+0= +github.com/spf13/cobra v1.10.0/go.mod h1:9dhySC7dnTtEiqzmqfkLj47BslqLCUPMXjG2lj/NgoE= +github.com/spf13/pflag v1.0.8/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -134,10 +147,16 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY= @@ -164,53 +183,30 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= -go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= -golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= +golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= +golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= +golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ= golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= -golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= -golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= -golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= +golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= +golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc= +golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y= +golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus= +golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM= golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= -golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q= +golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= @@ -226,40 +222,40 @@ google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= -gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= +gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM= -k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk= -k8s.io/apiextensions-apiserver v0.34.0 h1:B3hiB32jV7BcyKcMU5fDaDxk882YrJ1KU+ZSkA9Qxoc= -k8s.io/apiextensions-apiserver v0.34.0/go.mod h1:hLI4GxE1BDBy9adJKxUxCEHBGZtGfIg98Q+JmTD7+g0= -k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4= -k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= -k8s.io/apiserver v0.34.0 h1:Z51fw1iGMqN7uJ1kEaynf2Aec1Y774PqU+FVWCFV3Jg= -k8s.io/apiserver v0.34.0/go.mod h1:52ti5YhxAvewmmpVRqlASvaqxt0gKJxvCeW7ZrwgazQ= -k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY= -k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8= -k8s.io/component-base v0.34.0 h1:bS8Ua3zlJzapklsB1dZgjEJuJEeHjj8yTu1gxE2zQX8= -k8s.io/component-base v0.34.0/go.mod h1:RSCqUdvIjjrEm81epPcjQ/DS+49fADvGSCkIP3IC6vg= +k8s.io/api v0.35.0 h1:iBAU5LTyBI9vw3L5glmat1njFK34srdLmktWwLTprlY= +k8s.io/api v0.35.0/go.mod h1:AQ0SNTzm4ZAczM03QH42c7l3bih1TbAXYo0DkF8ktnA= +k8s.io/apiextensions-apiserver v0.35.0 h1:3xHk2rTOdWXXJM+RDQZJvdx0yEOgC0FgQ1PlJatA5T4= +k8s.io/apiextensions-apiserver v0.35.0/go.mod h1:E1Ahk9SADaLQ4qtzYFkwUqusXTcaV2uw3l14aqpL2LU= +k8s.io/apimachinery v0.35.0 h1:Z2L3IHvPVv/MJ7xRxHEtk6GoJElaAqDCCU0S6ncYok8= +k8s.io/apimachinery v0.35.0/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= +k8s.io/apiserver v0.35.0 h1:CUGo5o+7hW9GcAEF3x3usT3fX4f9r8xmgQeCBDaOgX4= +k8s.io/apiserver v0.35.0/go.mod h1:QUy1U4+PrzbJaM3XGu2tQ7U9A4udRRo5cyxkFX0GEds= +k8s.io/client-go v0.35.0 h1:IAW0ifFbfQQwQmga0UdoH0yvdqrbwMdq9vIFEhRpxBE= +k8s.io/client-go v0.35.0/go.mod h1:q2E5AAyqcbeLGPdoRB+Nxe3KYTfPce1Dnu1myQdqz9o= +k8s.io/component-base v0.35.0 h1:+yBrOhzri2S1BVqyVSvcM3PtPyx5GUxCK2tinZz1G94= +k8s.io/component-base v0.35.0/go.mod h1:85SCX4UCa6SCFt6p3IKAPej7jSnF3L8EbfSyMZayJR0= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA= -k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts= -k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y= -k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= +k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= -sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= -sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= -sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= -sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/controller-runtime v0.23.3 h1:VjB/vhoPoA9l1kEKZHBMnQF33tdCLQKJtydy4iqwZ80= +sigs.k8s.io/controller-runtime v0.23.3/go.mod h1:B6COOxKptp+YaUT5q4l6LqUJTRpizbgf9KSRNdQGns0= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= -sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= -sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482 h1:2WOzJpHUBVrrkDjU4KBT8n5LDcj824eX0I5UKcgeRUs= +sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/internal/controller/common.go b/internal/controller/common.go index 8823e28a..a9593ec6 100644 --- a/internal/controller/common.go +++ b/internal/controller/common.go @@ -80,9 +80,6 @@ const ( // Default RVPS reference values Path rvpsReferenceValuesPath = confidentialContainersPath + "/storage/local_json" - // TDX config file - tdxConfigFile = "sgx_default_qcnl.conf" - // IBM SE path ibmSePath = "/run/confidential-containers/ibmse/" @@ -99,6 +96,24 @@ const ( kbsDefaultLocalCacheDir = "/opt/confidential-containers/attestation-service/kds-store/vcek" ) +func standardLabels(instanceName, component string) map[string]string { + return map[string]string{ + "app.kubernetes.io/managed-by": "trustee-operator", + "app.kubernetes.io/part-of": "trustee", + "app.kubernetes.io/instance": instanceName, + "app.kubernetes.io/component": component, + } +} + +func hasStandardLabels(existing map[string]string, expected map[string]string) bool { + for k, v := range expected { + if existing[k] != v { + return false + } + } + return true +} + func contains(list []string, s string) bool { for _, v := range list { if v == s { diff --git a/internal/controller/ibmse_helper.go b/internal/controller/ibmse_helper.go new file mode 100644 index 00000000..84ac2fdb --- /dev/null +++ b/internal/controller/ibmse_helper.go @@ -0,0 +1,123 @@ +/* +Copyright Confidential Containers Contributors. + +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 controllers + +import ( + "context" + "fmt" + + corev1 "k8s.io/api/core/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// isIBMSE returns true if IBM SE configuration is specified +func (r *TrusteeConfigReconciler) isIBMSE() bool { + return r.trusteeConfig.Spec.IbmSE != nil +} + +// getIBMSEPVCName returns the auto-generated PVC name for IBM SE +func (r *TrusteeConfigReconciler) getIBMSEPVCName() string { + return r.trusteeConfig.Name + "-ibmse-certstore-pvc" +} + +// generateIBMSEPVC generates the PersistentVolumeClaim for IBM SE. +// The PVC binds to the PV named in spec.ibmSEPVName, which must be +// pre-created by the cluster administrator. +func (r *TrusteeConfigReconciler) generateIBMSEPVC() *corev1.PersistentVolumeClaim { + pvcName := r.getIBMSEPVCName() + // Match the PV's empty storageClassName to avoid default StorageClass injection. + sc := "" + return &corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: pvcName, + Namespace: r.namespace, + }, + Spec: corev1.PersistentVolumeClaimSpec{ + StorageClassName: &sc, + AccessModes: []corev1.PersistentVolumeAccessMode{ + corev1.ReadOnlyMany, + }, + Resources: corev1.VolumeResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: resource.MustParse("100Mi"), + }, + }, + VolumeName: r.trusteeConfig.Spec.IbmSE.PVName, + }, + } +} + +// validateIBMSEPV checks that the PV named in spec.ibmSE.pvName actually exists. +// This gives the user an actionable error early rather than leaving the PVC in +// Pending state indefinitely with no explanation. +func (r *TrusteeConfigReconciler) validateIBMSEPV(ctx context.Context) error { + pvName := r.trusteeConfig.Spec.IbmSE.PVName + if pvName == "" { + return fmt.Errorf("spec.ibmSE.pvName must be set when ibmSE is configured") + } + pv := &corev1.PersistentVolume{} + err := r.Get(ctx, client.ObjectKey{Name: pvName}, pv) + if k8serrors.IsNotFound(err) { + return fmt.Errorf("PersistentVolume %q not found — create the PV before applying the TrusteeConfig", pvName) + } + if err != nil { + return fmt.Errorf("failed to get PersistentVolume %q: %w", pvName, err) + } + return nil +} + +// createOrUpdateIBMSEPVC creates or updates the PersistentVolumeClaim for IBM SE. +// The PVC is owned by the TrusteeConfig CR and is garbage-collected when it is deleted. +func (r *TrusteeConfigReconciler) createOrUpdateIBMSEPVC(ctx context.Context) error { + if err := r.validateIBMSEPV(ctx); err != nil { + return err + } + + pvcName := r.getIBMSEPVCName() + desired := r.generateIBMSEPVC() + + if err := ctrl.SetControllerReference(r.trusteeConfig, desired, r.Scheme); err != nil { + return fmt.Errorf("failed to set controller reference on IBM SE PVC: %w", err) + } + + found := &corev1.PersistentVolumeClaim{} + err := r.Get(ctx, client.ObjectKey{ + Namespace: r.namespace, + Name: pvcName, + }, found) + + if err != nil && k8serrors.IsNotFound(err) { + r.log.Info("Creating IBM SE PersistentVolumeClaim", "PVC.Namespace", r.namespace, "PVC.Name", pvcName) + if err := r.Create(ctx, desired); err != nil { + return fmt.Errorf("failed to create IBM SE PVC: %w", err) + } + return nil + } else if err != nil { + return fmt.Errorf("failed to get IBM SE PVC: %w", err) + } + + if found.Spec.VolumeName != desired.Spec.VolumeName { + return fmt.Errorf("existing IBM SE PVC %s/%s is bound to volume %q, expected %q", r.namespace, pvcName, found.Spec.VolumeName, desired.Spec.VolumeName) + } + + r.log.V(1).Info("IBM SE PersistentVolumeClaim reconciled", "PVC.Namespace", r.namespace, "PVC.Name", pvcName) + return nil +} diff --git a/internal/controller/kbsconfig_controller.go b/internal/controller/kbsconfig_controller.go index 628255a3..cc80b066 100644 --- a/internal/controller/kbsconfig_controller.go +++ b/internal/controller/kbsconfig_controller.go @@ -21,17 +21,19 @@ import ( "fmt" "os" "path/filepath" + "sort" "time" configv1 "github.com/openshift/api/config/v1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + apiequality "k8s.io/apimachinery/pkg/api/equality" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/client-go/tools/record" + "k8s.io/client-go/tools/events" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" @@ -48,7 +50,7 @@ import ( type KbsConfigReconciler struct { client.Client Scheme *runtime.Scheme - Recorder record.EventRecorder + Recorder events.EventRecorder kbsConfig *confidentialcontainersorgv1alpha1.KbsConfig log logr.Logger namespace string @@ -198,11 +200,11 @@ func (r *KbsConfigReconciler) deployOrUpdateKbsService(ctx context.Context) erro } err = r.Create(ctx, service) if err != nil { - r.Recorder.Event(r.kbsConfig, corev1.EventTypeWarning, "ServiceCreateFailed", err.Error()) + r.Recorder.Eventf(r.kbsConfig, nil, corev1.EventTypeWarning, "ServiceCreateFailed", "ServiceCreateFailed", err.Error()) return err } // Service created successfully - return and requeue - r.Recorder.Event(r.kbsConfig, corev1.EventTypeNormal, "ServiceCreated", "KBS service created successfully") + r.Recorder.Eventf(r.kbsConfig, nil, corev1.EventTypeNormal, "ServiceCreated", "ServiceCreated", "KBS service created successfully") return nil } else if err != nil { return err @@ -217,7 +219,7 @@ func (r *KbsConfigReconciler) deployOrUpdateKbsService(ctx context.Context) erro } err = r.Update(ctx, service) if err != nil { - r.Recorder.Event(r.kbsConfig, corev1.EventTypeWarning, "ServiceUpdateFailed", err.Error()) + r.Recorder.Eventf(r.kbsConfig, nil, corev1.EventTypeWarning, "ServiceUpdateFailed", "ServiceUpdateFailed", err.Error()) return err } // Service updated successfully - ret @@ -288,12 +290,12 @@ func (r *KbsConfigReconciler) deployOrUpdateKbsDeployment(ctx context.Context) ( } err = r.Create(ctx, deployment) if err != nil { - r.Recorder.Event(r.kbsConfig, corev1.EventTypeWarning, "DeploymentCreateFailed", err.Error()) + r.Recorder.Eventf(r.kbsConfig, nil, corev1.EventTypeWarning, "DeploymentCreateFailed", "DeploymentCreateFailed", err.Error()) return false, err } // Deployment created successfully r.log.Info("Created a new deployment", "Deployment.Namespace", r.namespace, "Deployment.Name", KbsDeploymentName) - r.Recorder.Event(r.kbsConfig, corev1.EventTypeNormal, "DeploymentCreated", "Trustee deployment created successfully") + r.Recorder.Eventf(r.kbsConfig, nil, corev1.EventTypeNormal, "DeploymentCreated", "DeploymentCreated", "Trustee deployment created successfully") // Add the kbsFinalizer to the KbsConfig if it doesn't already exist return true, r.addKbsConfigFinalizer(ctx) } else if err != nil { @@ -301,14 +303,15 @@ func (r *KbsConfigReconciler) deployOrUpdateKbsDeployment(ctx context.Context) ( return false, err } // Update the found deployment and write the result back if there are any changes - err = r.updateKbsDeployment(ctx, found) + updated, err := r.updateKbsDeployment(ctx, found) if err != nil { - r.Recorder.Event(r.kbsConfig, corev1.EventTypeWarning, "DeploymentUpdateFailed", err.Error()) + r.Recorder.Eventf(r.kbsConfig, nil, corev1.EventTypeWarning, "DeploymentUpdateFailed", "DeploymentUpdateFailed", err.Error()) return false, err } - // Deployment updated successfully - r.log.Info("Updated Deployment", "Deployment.Namespace", r.namespace, "Deployment.Name", KbsDeploymentName) - r.Recorder.Event(r.kbsConfig, corev1.EventTypeNormal, "DeploymentUpdated", "Trustee deployment updated successfully") + if updated { + r.log.Info("Updated Deployment", "Deployment.Namespace", r.namespace, "Deployment.Name", KbsDeploymentName) + r.Recorder.Eventf(r.kbsConfig, nil, corev1.EventTypeNormal, "DeploymentUpdated", "DeploymentUpdated", "Trustee deployment updated successfully") + } return false, nil } @@ -439,17 +442,6 @@ func (r *KbsConfigReconciler) newKbsDeployment(ctx context.Context) (*appsv1.Dep kbsVM = append(kbsVM, volumeMount) } - // TDX specific configuration - if r.kbsConfig.Spec.TdxConfigSpec.KbsTdxConfigMapName != "" { - volume, err = r.createConfigMapVolume(ctx, "tdx-config", r.kbsConfig.Spec.TdxConfigSpec.KbsTdxConfigMapName) - if err != nil { - return nil, err - } - volumeMount = createVolumeMountWithSubpath(volume.Name, filepath.Join(kbsDefaultConfigPath, tdxConfigFile), tdxConfigFile) - volumes = append(volumes, *volume) - kbsVM = append(kbsVM, volumeMount) - } - // IBMSE specific configuration if r.kbsConfig.Spec.IbmSEConfigSpec.CertStorePvc != "" { volume, err := r.createPVCVolume(ctx, r.kbsConfig.Spec.IbmSEConfigSpec.CertStorePvc) @@ -637,7 +629,8 @@ func (r *KbsConfigReconciler) newKbsDeployment(ctx context.Context) (*appsv1.Dep }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: labels, + Labels: labels, + Annotations: r.getConfigMapVersionAnnotations(ctx), }, // Add the KBS container Spec: corev1.PodSpec{ @@ -792,6 +785,39 @@ func (r *KbsConfigReconciler) buildKbsContainer(volumeMounts []corev1.VolumeMoun "/etc/kbs-config/kbs-config.toml", } + probeScheme := corev1.URISchemeHTTP + if r.isHttpsConfigPresent() { + probeScheme = corev1.URISchemeHTTPS + } + + healthProbe := &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/healthz", + Port: intstr.FromInt32(8080), + Scheme: probeScheme, + }, + }, + InitialDelaySeconds: 5, + PeriodSeconds: 10, + TimeoutSeconds: 5, + FailureThreshold: 3, + } + + livenessProbe := &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/healthz", + Port: intstr.FromInt32(8080), + Scheme: probeScheme, + }, + }, + InitialDelaySeconds: 15, + PeriodSeconds: 30, + TimeoutSeconds: 5, + FailureThreshold: 3, + } + return corev1.Container{ Name: "kbs", Image: imageName, @@ -805,8 +831,10 @@ func (r *KbsConfigReconciler) buildKbsContainer(volumeMounts []corev1.VolumeMoun Command: command, SecurityContext: securityContext, // Add volume mount for KBS config - VolumeMounts: volumeMounts, - Env: env, + VolumeMounts: volumeMounts, + Env: env, + ReadinessProbe: healthProbe, + LivenessProbe: livenessProbe, } } @@ -866,10 +894,13 @@ func buildEnvVars(r *KbsConfigReconciler, ctx context.Context) []corev1.EnvVar { } } - // Convert map to array + // Convert map to array with stable ordering for k, v := range envVarsMap { env = append(env, corev1.EnvVar{Name: k, Value: v}) } + sort.Slice(env, func(i, j int) bool { + return env[i].Name < env[j].Name + }) return env } @@ -888,28 +919,86 @@ func (r *KbsConfigReconciler) isAttestationConfigPresent() bool { return false } +// getConfigMapVersionAnnotations collects ResourceVersions of all mounted ConfigMaps +// and returns them as pod template annotations. When any ConfigMap changes, Kubernetes +// increments its ResourceVersion, which updates the annotation and triggers a rolling +// restart of the KBS pods. +// +// This ensures KBS pods automatically restart when ANY configuration changes: +// - Trustee configuration (KbsConfigMapName) +// - Attestation policies (KbsAttestationPolicyConfigMapName, KbsGpuAttestationPolicyConfigMapName) +// - Reference values (KbsRvpsRefValuesConfigMapName) +// - Resource policies (KbsResourcePolicyConfigMapName) +func (r *KbsConfigReconciler) getConfigMapVersionAnnotations(ctx context.Context) map[string]string { + annotations := make(map[string]string) + + // List of all ConfigMaps that Trustee mounts + // These must match the ConfigMaps used in newKbsDeployment() + configMapNames := []string{ + r.kbsConfig.Spec.KbsConfigMapName, + r.kbsConfig.Spec.KbsRvpsRefValuesConfigMapName, + r.kbsConfig.Spec.KbsAttestationPolicyConfigMapName, + r.kbsConfig.Spec.KbsGpuAttestationPolicyConfigMapName, + r.kbsConfig.Spec.KbsResourcePolicyConfigMapName, + } + + var versions []string + for _, cmName := range configMapNames { + // Skip empty ConfigMap names (optional ConfigMaps) + if cmName == "" { + continue + } + + // Fetch the ConfigMap to get its ResourceVersion + configMap := &corev1.ConfigMap{} + err := r.Get(ctx, client.ObjectKey{Namespace: r.namespace, Name: cmName}, configMap) + if err != nil { + // If ConfigMap doesn't exist, skip it (might be optional or not created yet) + r.log.V(1).Info("ConfigMap not found for version tracking", "name", cmName, "error", err) + continue + } + + // Append "name:version" to the list + versions = append(versions, fmt.Sprintf("%s:%s", cmName, configMap.ResourceVersion)) + } + + // Combine all versions into a single annotation + // Format: "kbs-config:12345,reference-values:67890,..." + if len(versions) > 0 { + annotations["kbs.confidentialcontainers.org/configmap-versions"] = versions[0] + for _, v := range versions[1:] { + annotations["kbs.confidentialcontainers.org/configmap-versions"] += "," + v + } + } + + return annotations +} + // updateKbsDeployment updates an existing deployment for the KBS instance // Errors are logged by the callee and hence no error is logged in this method -func (r *KbsConfigReconciler) updateKbsDeployment(ctx context.Context, deployment *appsv1.Deployment) error { +func (r *KbsConfigReconciler) updateKbsDeployment(ctx context.Context, deployment *appsv1.Deployment) (bool, error) { // re-generates the deployment newDeployment, err := r.newKbsDeployment(ctx) if err != nil { - return err + return false, err + } + + desiredTemplate := newDeployment.Spec.Template.DeepCopy() + desiredReplicas := newDeployment.Spec.Replicas + + if apiequality.Semantic.DeepEqual(deployment.Spec.Template, *desiredTemplate) && + apiequality.Semantic.DeepEqual(deployment.Spec.Replicas, desiredReplicas) { + return false, nil } - // overwrites the template spec, if any changes - deployment.Spec.Template.Spec = *newDeployment.Spec.Template.Spec.DeepCopy() - // Update replicas if changed - deployment.Spec.Replicas = newDeployment.Spec.Replicas + deployment.Spec.Template = *desiredTemplate + deployment.Spec.Replicas = desiredReplicas err = r.Update(ctx, deployment) if err != nil { - return err - } else { - // Deployment updated successfully - r.log.Info("Updated Deployment", "Deployment.Namespace", r.namespace, "Deployment.Name", "kbs-deployment") - return nil + return false, err } + return true, nil } // SetupWithManager sets up the controller with the Manager. @@ -926,7 +1015,7 @@ func (r *KbsConfigReconciler) SetupWithManager(mgr ctrl.Manager) error { r.log = r.log.WithValues("kbsconfig", r.namespace) // Create an event recorder for emitting Kubernetes events - r.Recorder = mgr.GetEventRecorderFor("kbsconfig-controller") + r.Recorder = mgr.GetEventRecorder("kbsconfig-controller") configMapMapper, err := configMapToKbsConfigMapper(r.Client, r.log) if err != nil { @@ -992,8 +1081,7 @@ func configMapToKbsConfigMapper(c client.Client, log logr.Logger) (handler.MapFu kbsConfig.Spec.KbsRvpsRefValuesConfigMapName == configMap.Name || kbsConfig.Spec.KbsAttestationPolicyConfigMapName == configMap.Name || kbsConfig.Spec.KbsGpuAttestationPolicyConfigMapName == configMap.Name || - kbsConfig.Spec.KbsResourcePolicyConfigMapName == configMap.Name || - kbsConfig.Spec.TdxConfigSpec.KbsTdxConfigMapName == configMap.Name { + kbsConfig.Spec.KbsResourcePolicyConfigMapName == configMap.Name { requests = append(requests, reconcile.Request{ NamespacedName: types.NamespacedName{ @@ -1107,9 +1195,9 @@ func (r *KbsConfigReconciler) updateKbsConfigStatus(ctx context.Context) error { newIsReady = deployment.Status.ReadyReplicas >= 1 && deployment.Status.ReadyReplicas == deployment.Status.Replicas r.log.Info("Checked KbsConfig status", "IsReady", newIsReady, "ReadyReplicas", deployment.Status.ReadyReplicas, "Replicas", deployment.Status.Replicas, "AvailableReplicas", deployment.Status.AvailableReplicas, "UpdatedReplicas", deployment.Status.UpdatedReplicas) if newIsReady && !oldIsReady { - r.Recorder.Event(r.kbsConfig, corev1.EventTypeNormal, "Ready", "Trustee deployment is ready") + r.Recorder.Eventf(r.kbsConfig, nil, corev1.EventTypeNormal, "Ready", "Ready", "Trustee deployment is ready") } else if !newIsReady && oldIsReady { - r.Recorder.Event(r.kbsConfig, corev1.EventTypeWarning, "NotReady", "Trustee deployment is no longer ready") + r.Recorder.Eventf(r.kbsConfig, nil, corev1.EventTypeWarning, "NotReady", "NotReady", "Trustee deployment is no longer ready") } } diff --git a/internal/controller/resource_policy_helper.go b/internal/controller/resource_policy_helper.go index 9899819b..2ec44cc4 100644 --- a/internal/controller/resource_policy_helper.go +++ b/internal/controller/resource_policy_helper.go @@ -20,18 +20,24 @@ import ( "os" ) -// generateResourcePolicyRego generates the Rego policy content based on profile type -func generateResourcePolicyRego(profileType string) (string, error) { +// generateResourcePolicyRego generates the Rego policy content based on tee type and profile type. +// For IBM SE, the IBM SE-specific template is always used regardless of profile. +func generateResourcePolicyRego(isIBMSE bool, profileType string) (string, error) { var templateFile string - // Select template file based on profile type - switch profileType { - case "Restricted": - templateFile = "/config/templates/resource-policy-restrictive.rego" - case "Permissive": - templateFile = "/config/templates/resource-policy-permissive.rego" - default: - templateFile = "/config/templates/resource-policy-permissive.rego" + // IBM SE requires its own resource policy template regardless of profile. + if isIBMSE { + templateFile = "/config/templates/resource-policy-ibm.rego" + } else { + // Select template file based on profile type + switch profileType { + case "Restricted": + templateFile = "/config/templates/resource-policy-restrictive.rego" + case "Permissive": + templateFile = "/config/templates/resource-policy-permissive.rego" + default: + templateFile = "/config/templates/resource-policy-permissive.rego" + } } // Read the template file diff --git a/internal/controller/tdx_helper.go b/internal/controller/tdx_helper.go deleted file mode 100644 index fe144934..00000000 --- a/internal/controller/tdx_helper.go +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright Confidential Containers Contributors. - -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 controllers - -import ( - "os" -) - -// generateTdxConfigJson generates the TDX config JSON content from template -func generateTdxConfigJson() (string, error) { - templateFile := "/config/templates/tdx-config.json" - - // Read the template file - configBytes, err := os.ReadFile(templateFile) - if err != nil { - return "", err - } - - return string(configBytes), nil -} diff --git a/internal/controller/trusteeconfig_controller.go b/internal/controller/trusteeconfig_controller.go index b923bac5..f8154768 100644 --- a/internal/controller/trusteeconfig_controller.go +++ b/internal/controller/trusteeconfig_controller.go @@ -54,8 +54,10 @@ type TrusteeConfigReconciler struct { //+kubebuilder:rbac:groups=confidentialcontainers.org,resources=trusteeconfigs/finalizers,verbs=update //+kubebuilder:rbac:groups=confidentialcontainers.org,resources=kbsconfigs,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=confidentialcontainers.org,resources=kbsconfigs/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=core,resources=configmaps,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups="",resources=persistentvolumes,verbs=get;list;watch +//+kubebuilder:rbac:groups="",resources=persistentvolumeclaims,verbs=get;list;watch;create;update;delete +//+kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. @@ -68,8 +70,6 @@ func (r *TrusteeConfigReconciler) Reconcile(ctx context.Context, req ctrl.Reques err := r.Get(ctx, req.NamespacedName, r.trusteeConfig) if err != nil { if k8serrors.IsNotFound(err) { - // Request object not found, could have been deleted after reconcile request. - // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. r.log.Info("TrusteeConfig resource not found. Ignoring since object must be deleted.") return ctrl.Result{}, nil } @@ -141,12 +141,13 @@ func (r *TrusteeConfigReconciler) SetupWithManager(mgr ctrl.Manager) error { &confidentialcontainersorgv1alpha1.KbsConfig{}, handler.EnqueueRequestForOwner(mgr.GetScheme(), mgr.GetRESTMapper(), &confidentialcontainersorgv1alpha1.TrusteeConfig{}), ). - // Watch owned ConfigMaps and Secrets so that accidental deletion triggers - // reconcile and the controller recreates them. Safe because all - // createOrUpdate helpers are create-once: they never call r.Update on - // an existing resource, so no update loop can form. + // Watch owned ConfigMaps, Secrets, and PVCs so that accidental deletion + // triggers reconcile and the controller recreates them. Updates may still + // occur (e.g., PVC adoption); reconciliation is idempotent so any watch- + // triggered update loops converge quickly. Owns(&corev1.ConfigMap{}). Owns(&corev1.Secret{}). + Owns(&corev1.PersistentVolumeClaim{}). Complete(r) } @@ -245,8 +246,12 @@ func (r *TrusteeConfigReconciler) detectManualChanges(current, generated confide // Custom local cert cache len(current.KbsLocalCertCacheSpec.Secrets) > 0 && !r.certCacheSpecsEqual(current.KbsLocalCertCacheSpec, generated.KbsLocalCertCacheSpec), - // Custom IBM SE config - current.IbmSEConfigSpec.CertStorePvc != "" && current.IbmSEConfigSpec.CertStorePvc != generated.IbmSEConfigSpec.CertStorePvc, + // Custom IBM SE PVC: treat as a manual override only when the user has + // set a PVC name that is neither empty nor the one auto-provisioned by + // this operator, so our own writes are never mistaken for user edits. + current.IbmSEConfigSpec.CertStorePvc != "" && + current.IbmSEConfigSpec.CertStorePvc != generated.IbmSEConfigSpec.CertStorePvc && + current.IbmSEConfigSpec.CertStorePvc != r.getIBMSEPVCName(), } // Return true if any user-configurable field has been modified @@ -328,8 +333,10 @@ func (r *TrusteeConfigReconciler) mergeKbsConfigSpecs(generatedSpec, manualSpec merged.KbsLocalCertCacheSpec.Secrets = manualSpec.KbsLocalCertCacheSpec.Secrets } - // Preserve manual IBM SE configuration - if manualSpec.IbmSEConfigSpec.CertStorePvc != "" { + // Only preserve a user-supplied IBM SE PVC name — skip the auto-provisioned + // one so the operator can clear IBM SE wiring when ibmSE is removed from the spec. + if manualSpec.IbmSEConfigSpec.CertStorePvc != "" && + manualSpec.IbmSEConfigSpec.CertStorePvc != r.getIBMSEPVCName() { merged.IbmSEConfigSpec.CertStorePvc = manualSpec.IbmSEConfigSpec.CertStorePvc } @@ -341,6 +348,22 @@ func (r *TrusteeConfigReconciler) mergeKbsConfigSpecs(generatedSpec, manualSpec return merged } +// ensureSecretLabels patches standard labels onto an existing secret if missing. +func (r *TrusteeConfigReconciler) ensureSecretLabels(ctx context.Context, secret *corev1.Secret, component string) error { + expected := standardLabels(r.trusteeConfig.Name, component) + if hasStandardLabels(secret.Labels, expected) { + return nil + } + if secret.Labels == nil { + secret.Labels = make(map[string]string) + } + for k, v := range expected { + secret.Labels[k] = v + } + r.log.Info("Patching standard labels onto existing secret", "Secret.Name", secret.Name) + return r.Update(ctx, secret) +} + // buildKbsConfigSpec builds the KbsConfigSpec based on TrusteeConfig. // Returns an error if any required resource cannot be created so that // Reconcile can return the error and let controller-runtime retry rather @@ -376,6 +399,15 @@ func (r *TrusteeConfigReconciler) buildKbsConfigSpec(ctx context.Context) (confi return spec, err } + // Configure IBM SE PVC after profile configuration (applies to all profiles). + // The PV must be pre-created by the cluster administrator and named in spec.ibmSEPVName. + if r.isIBMSE() { + if err = r.createOrUpdateIBMSEPVC(ctx); err != nil { + return spec, fmt.Errorf("IBM SE PVC: %w", err) + } + spec.IbmSEConfigSpec.CertStorePvc = r.getIBMSEPVCName() + } + // Configure HTTPS if specified if r.trusteeConfig.Spec.HttpsSpec.TlsSecretName != "" { if err = r.createOrUpdateHttpsSecrets(ctx); err != nil { @@ -426,20 +458,17 @@ func (r *TrusteeConfigReconciler) configurePermissiveProfile(ctx context.Context } spec.KbsRvpsRefValuesConfigMapName = r.getRvpsReferenceValuesConfigMapName() - if err := r.createOrUpdateTdxConfigMap(ctx); err != nil { - return spec, fmt.Errorf("TDX ConfigMap: %w", err) - } - spec.TdxConfigSpec.KbsTdxConfigMapName = r.getTdxConfigMapName() - - if err := r.createOrUpdateAttestationPolicyConfigMap(ctx); err != nil { - return spec, fmt.Errorf("CPU attestation policy ConfigMap: %w", err) - } - spec.KbsAttestationPolicyConfigMapName = r.getCpuAttestationPolicyConfigMapName() - - if err := r.createOrUpdateGpuAttestationPolicyConfigMap(ctx); err != nil { - return spec, fmt.Errorf("GPU attestation policy ConfigMap: %w", err) + // CPU/GPU attestation policies are not applicable to IBM SE deployments. + if !r.isIBMSE() { + if err := r.createOrUpdateAttestationPolicyConfigMap(ctx); err != nil { + return spec, fmt.Errorf("CPU attestation policy ConfigMap: %w", err) + } + spec.KbsAttestationPolicyConfigMapName = r.getCpuAttestationPolicyConfigMapName() + if err := r.createOrUpdateGpuAttestationPolicyConfigMap(ctx); err != nil { + return spec, fmt.Errorf("GPU attestation policy ConfigMap: %w", err) + } + spec.KbsGpuAttestationPolicyConfigMapName = r.getGpuAttestationPolicyConfigMapName() } - spec.KbsGpuAttestationPolicyConfigMapName = r.getGpuAttestationPolicyConfigMapName() return spec, nil } @@ -474,20 +503,17 @@ func (r *TrusteeConfigReconciler) configureRestrictedProfile(ctx context.Context } spec.KbsRvpsRefValuesConfigMapName = r.getRvpsReferenceValuesConfigMapName() - if err := r.createOrUpdateTdxConfigMap(ctx); err != nil { - return spec, fmt.Errorf("TDX ConfigMap: %w", err) - } - spec.TdxConfigSpec.KbsTdxConfigMapName = r.getTdxConfigMapName() - - if err := r.createOrUpdateAttestationPolicyConfigMap(ctx); err != nil { - return spec, fmt.Errorf("CPU attestation policy ConfigMap: %w", err) - } - spec.KbsAttestationPolicyConfigMapName = r.getCpuAttestationPolicyConfigMapName() - - if err := r.createOrUpdateGpuAttestationPolicyConfigMap(ctx); err != nil { - return spec, fmt.Errorf("GPU attestation policy ConfigMap: %w", err) + // CPU/GPU attestation policies are not applicable to IBM SE deployments. + if !r.isIBMSE() { + if err := r.createOrUpdateAttestationPolicyConfigMap(ctx); err != nil { + return spec, fmt.Errorf("CPU attestation policy ConfigMap: %w", err) + } + spec.KbsAttestationPolicyConfigMapName = r.getCpuAttestationPolicyConfigMapName() + if err := r.createOrUpdateGpuAttestationPolicyConfigMap(ctx); err != nil { + return spec, fmt.Errorf("GPU attestation policy ConfigMap: %w", err) + } + spec.KbsGpuAttestationPolicyConfigMapName = r.getGpuAttestationPolicyConfigMapName() } - spec.KbsGpuAttestationPolicyConfigMapName = r.getGpuAttestationPolicyConfigMapName() return spec, nil } @@ -673,6 +699,7 @@ func (r *TrusteeConfigReconciler) generateKbsAuthSecret(ctx context.Context) (*c ObjectMeta: metav1.ObjectMeta{ Name: secretName, Namespace: r.namespace, + Labels: standardLabels(r.trusteeConfig.Name, "auth"), }, Type: corev1.SecretTypeOpaque, Data: data, @@ -699,6 +726,7 @@ func (r *TrusteeConfigReconciler) generateKbsSampleSecret(ctx context.Context) ( ObjectMeta: metav1.ObjectMeta{ Name: secretName, Namespace: r.namespace, + Labels: standardLabels(r.trusteeConfig.Name, "sample"), }, Type: corev1.SecretTypeOpaque, Data: data, @@ -748,8 +776,11 @@ func (r *TrusteeConfigReconciler) createOrUpdateKbsAuthSecret(ctx context.Contex } else if err != nil { return err } else { - // Secret already exists, preserve its content + // Secret already exists, preserve its content and ensure labels are added r.log.Info("KBS auth secret already exists, preserving existing content", "Secret.Namespace", r.namespace, "Secret.Name", secretName) + if err := r.ensureSecretLabels(ctx, found, "auth"); err != nil { + return err + } } return nil @@ -780,8 +811,11 @@ func (r *TrusteeConfigReconciler) createOrUpdateKbsSampleSecret(ctx context.Cont } else if err != nil { return err } else { - // Secret already exists, preserve its content + // Secret already exists, preserve its content and ensure labels are added r.log.Info("KBS sample secret already exists, preserving existing content", "Secret.Namespace", r.namespace, "Secret.Name", secretName) + if err := r.ensureSecretLabels(ctx, found, "sample"); err != nil { + return err + } } return nil @@ -862,8 +896,11 @@ func (r *TrusteeConfigReconciler) createOrUpdateHttpsKeySecret(ctx context.Conte } else if err != nil { return err } else { - // Secret already exists, preserve its content + // Secret already exists, preserve its content and ensure labels are added r.log.Info("HTTPS key secret already exists, preserving existing content", "Secret.Namespace", r.namespace, "Secret.Name", secretName) + if err := r.ensureSecretLabels(ctx, found, "https"); err != nil { + return err + } } return nil @@ -893,8 +930,11 @@ func (r *TrusteeConfigReconciler) createOrUpdateHttpsCertSecret(ctx context.Cont } else if err != nil { return err } else { - // Secret already exists, preserve its content + // Secret already exists, preserve its content and ensure labels are added r.log.Info("HTTPS certificate secret already exists, preserving existing content", "Secret.Namespace", r.namespace, "Secret.Name", secretName) + if err := r.ensureSecretLabels(ctx, found, "https"); err != nil { + return err + } } return nil @@ -911,6 +951,7 @@ func (r *TrusteeConfigReconciler) generateHttpsKeySecret(keyData []byte) (*corev ObjectMeta: metav1.ObjectMeta{ Name: secretName, Namespace: r.namespace, + Labels: standardLabels(r.trusteeConfig.Name, "https"), }, Type: corev1.SecretTypeOpaque, Data: data, @@ -933,6 +974,7 @@ func (r *TrusteeConfigReconciler) generateHttpsCertSecret(certData []byte) (*cor ObjectMeta: metav1.ObjectMeta{ Name: secretName, Namespace: r.namespace, + Labels: standardLabels(r.trusteeConfig.Name, "https"), }, Type: corev1.SecretTypeOpaque, Data: data, @@ -1019,8 +1061,11 @@ func (r *TrusteeConfigReconciler) createOrUpdateAttestationKeySecret(ctx context } else if err != nil { return err } else { - // Secret already exists, preserve its content + // Secret already exists, preserve its content and ensure labels are added r.log.Info("Attestation key secret already exists, preserving existing content", "Secret.Namespace", r.namespace, "Secret.Name", secretName) + if err := r.ensureSecretLabels(ctx, found, "attestation"); err != nil { + return err + } } return nil @@ -1050,8 +1095,11 @@ func (r *TrusteeConfigReconciler) createOrUpdateAttestationCertSecret(ctx contex } else if err != nil { return err } else { - // Secret already exists, preserve its content + // Secret already exists, preserve its content and ensure labels are added r.log.Info("Attestation certificate secret already exists, preserving existing content", "Secret.Namespace", r.namespace, "Secret.Name", secretName) + if err := r.ensureSecretLabels(ctx, found, "attestation"); err != nil { + return err + } } return nil @@ -1068,6 +1116,7 @@ func (r *TrusteeConfigReconciler) generateAttestationCertSecret(certData []byte) ObjectMeta: metav1.ObjectMeta{ Name: secretName, Namespace: r.namespace, + Labels: standardLabels(r.trusteeConfig.Name, "attestation"), }, Type: corev1.SecretTypeOpaque, Data: data, @@ -1100,6 +1149,7 @@ func (r *TrusteeConfigReconciler) generateAttestationKeySecret(keyData []byte) ( ObjectMeta: metav1.ObjectMeta{ Name: secretName, Namespace: r.namespace, + Labels: standardLabels(r.trusteeConfig.Name, "attestation"), }, Type: corev1.SecretTypeOpaque, Data: data, @@ -1113,7 +1163,7 @@ func (r *TrusteeConfigReconciler) generateAttestationKeySecret(keyData []byte) ( // generateResourcePolicyConfigMap creates a ConfigMap for resource policy func (r *TrusteeConfigReconciler) generateResourcePolicyConfigMap(ctx context.Context) (*corev1.ConfigMap, error) { - policyRego, err := generateResourcePolicyRego(string(r.trusteeConfig.Spec.Profile)) + policyRego, err := generateResourcePolicyRego(r.isIBMSE(), string(r.trusteeConfig.Spec.Profile)) if err != nil { return nil, err } @@ -1215,58 +1265,6 @@ func (r *TrusteeConfigReconciler) createOrUpdateRvpsReferenceValuesConfigMap(ctx return nil } -// generateTdxConfigMap creates a ConfigMap for TDX configuration -func (r *TrusteeConfigReconciler) generateTdxConfigMap(ctx context.Context) (*corev1.ConfigMap, error) { - tdxConfigJson, err := generateTdxConfigJson() - if err != nil { - return nil, err - } - - configMap := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: r.getTdxConfigMapName(), - Namespace: r.namespace, - }, - Data: map[string]string{ - tdxConfigFile: tdxConfigJson, - }, - } - - err = ctrl.SetControllerReference(r.trusteeConfig, configMap, r.Scheme) - if err != nil { - return nil, err - } - - return configMap, nil -} - -// getTdxConfigMapName returns the name for the TDX config map -func (r *TrusteeConfigReconciler) getTdxConfigMapName() string { - return r.trusteeConfig.Name + "-tdx-config" -} - -// createOrUpdateTdxConfigMap creates or updates the TDX ConfigMap -func (r *TrusteeConfigReconciler) createOrUpdateTdxConfigMap(ctx context.Context) error { - configMapName := r.getTdxConfigMapName() - found := &corev1.ConfigMap{} - err := r.Get(ctx, client.ObjectKey{Namespace: r.namespace, Name: configMapName}, found) - - if err != nil && k8serrors.IsNotFound(err) { - r.log.Info("Creating TDX config map", "ConfigMap.Namespace", r.namespace, "ConfigMap.Name", configMapName) - configMap, err := r.generateTdxConfigMap(ctx) - if err != nil { - return err - } - return r.Create(ctx, configMap) - } else if err != nil { - return err - } - - // ConfigMap already exists, preserve its content - r.log.Info("TDX config map already exists, preserving existing content", "ConfigMap.Namespace", r.namespace, "ConfigMap.Name", configMapName) - return nil -} - // generateAttestationPolicyConfigMap creates a ConfigMap for CPU attestation policy func (r *TrusteeConfigReconciler) generateAttestationPolicyConfigMap(ctx context.Context) (*corev1.ConfigMap, error) { policyRego, err := generateCpuAttestationPolicyRego(string(r.trusteeConfig.Spec.Profile)) diff --git a/tests/e2e/sample-attester-https/03-kbs-config.yaml b/tests/e2e/sample-attester-https/03-kbs-config.yaml index 36987c39..9ac124f4 100644 --- a/tests/e2e/sample-attester-https/03-kbs-config.yaml +++ b/tests/e2e/sample-attester-https/03-kbs-config.yaml @@ -45,6 +45,9 @@ data: { type = "KDS" } ] + [attestation_service.verifier_config.dcap_verifier] + collateral_service = "https://api.trustedservices.intel.com/sgx/certification/v4/" + [[plugins]] name = "resource" storage_backend_type = "kvstorage" diff --git a/tests/e2e/sample-attester/03-kbs-config.yaml b/tests/e2e/sample-attester/03-kbs-config.yaml index b5edbdf2..6f8c43d8 100644 --- a/tests/e2e/sample-attester/03-kbs-config.yaml +++ b/tests/e2e/sample-attester/03-kbs-config.yaml @@ -43,6 +43,9 @@ data: { type = "KDS" } ] + [attestation_service.verifier_config.dcap_verifier] + collateral_service = "https://api.trustedservices.intel.com/sgx/certification/v4/" + [[plugins]] name = "resource" storage_backend_type = "kvstorage" diff --git a/tests/e2e/sample-trusteeconfig-restricted/15-assert-https-secrets.yaml b/tests/e2e/sample-trusteeconfig-restricted/15-assert-https-secrets.yaml index a5df1983..a632911e 100644 --- a/tests/e2e/sample-trusteeconfig-restricted/15-assert-https-secrets.yaml +++ b/tests/e2e/sample-trusteeconfig-restricted/15-assert-https-secrets.yaml @@ -3,21 +3,41 @@ kind: Secret metadata: name: trusteeconfig-restricted-https-key-secret namespace: trustee-operator-system + labels: + app.kubernetes.io/managed-by: trustee-operator + app.kubernetes.io/part-of: trustee + app.kubernetes.io/instance: trusteeconfig-restricted + app.kubernetes.io/component: https --- apiVersion: v1 kind: Secret metadata: name: trusteeconfig-restricted-https-cert-secret namespace: trustee-operator-system + labels: + app.kubernetes.io/managed-by: trustee-operator + app.kubernetes.io/part-of: trustee + app.kubernetes.io/instance: trusteeconfig-restricted + app.kubernetes.io/component: https --- apiVersion: v1 kind: Secret metadata: name: trusteeconfig-restricted-attestation-key-secret namespace: trustee-operator-system + labels: + app.kubernetes.io/managed-by: trustee-operator + app.kubernetes.io/part-of: trustee + app.kubernetes.io/instance: trusteeconfig-restricted + app.kubernetes.io/component: attestation --- apiVersion: v1 kind: Secret metadata: name: trusteeconfig-restricted-attestation-cert-secret namespace: trustee-operator-system + labels: + app.kubernetes.io/managed-by: trustee-operator + app.kubernetes.io/part-of: trustee + app.kubernetes.io/instance: trusteeconfig-restricted + app.kubernetes.io/component: attestation diff --git a/tests/e2e/sample-trusteeconfig-restricted/30-assert-auth-secret.yaml b/tests/e2e/sample-trusteeconfig-restricted/30-assert-auth-secret.yaml index afc83c7c..8c0a47a6 100644 --- a/tests/e2e/sample-trusteeconfig-restricted/30-assert-auth-secret.yaml +++ b/tests/e2e/sample-trusteeconfig-restricted/30-assert-auth-secret.yaml @@ -3,3 +3,8 @@ kind: Secret metadata: name: trusteeconfig-restricted-auth-secret namespace: trustee-operator-system + labels: + app.kubernetes.io/managed-by: trustee-operator + app.kubernetes.io/part-of: trustee + app.kubernetes.io/instance: trusteeconfig-restricted + app.kubernetes.io/component: auth diff --git a/tests/e2e/sample-trusteeconfig-restricted/60-assert-tdx-config.yaml b/tests/e2e/sample-trusteeconfig-restricted/60-assert-tdx-config.yaml deleted file mode 100644 index 361f359e..00000000 --- a/tests/e2e/sample-trusteeconfig-restricted/60-assert-tdx-config.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: trusteeconfig-restricted-tdx-config - namespace: trustee-operator-system diff --git a/tests/e2e/sample-trusteeconfig/30-assert-auth-secret.yaml b/tests/e2e/sample-trusteeconfig/30-assert-auth-secret.yaml index 40ad2097..ea77d9da 100644 --- a/tests/e2e/sample-trusteeconfig/30-assert-auth-secret.yaml +++ b/tests/e2e/sample-trusteeconfig/30-assert-auth-secret.yaml @@ -3,3 +3,8 @@ kind: Secret metadata: name: trusteeconfig-sample-auth-secret namespace: trustee-operator-system + labels: + app.kubernetes.io/managed-by: trustee-operator + app.kubernetes.io/part-of: trustee + app.kubernetes.io/instance: trusteeconfig-sample + app.kubernetes.io/component: auth diff --git a/tests/e2e/sample-trusteeconfig/60-assert-tdx-config.yaml b/tests/e2e/sample-trusteeconfig/60-assert-tdx-config.yaml deleted file mode 100644 index 254fdb11..00000000 --- a/tests/e2e/sample-trusteeconfig/60-assert-tdx-config.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: trusteeconfig-sample-tdx-config - namespace: trustee-operator-system diff --git a/tests/e2e/trusteeconfig-self-healing/30-assert.yaml b/tests/e2e/trusteeconfig-self-healing/30-assert.yaml index 738fcefd..688185d9 100644 --- a/tests/e2e/trusteeconfig-self-healing/30-assert.yaml +++ b/tests/e2e/trusteeconfig-self-healing/30-assert.yaml @@ -7,3 +7,8 @@ kind: Secret metadata: name: trusteeconfig-sample-auth-secret namespace: trustee-operator-system + labels: + app.kubernetes.io/managed-by: trustee-operator + app.kubernetes.io/part-of: trustee + app.kubernetes.io/instance: trusteeconfig-sample + app.kubernetes.io/component: auth