diff --git a/README.md b/README.md index c66d75f..b922e6e 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,17 @@ type KbsConfigSpec struct { // 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"` +} + +// IbmSEConfigSpec defines the desired state for IBMSE configuration +type IbmSEConfigSpec struct { + // certStorePvc is the name of the PeristentVolumeClaim where certificates/keys are mounted + // +optional + CertStorePvc string `json:"certStorePvc,omitempty"` } // TdxConfigSpec defines the desired state for TDX configuration @@ -156,8 +167,12 @@ spec: kbsSecretResources: ["kbsres1"] # Resource policy kbsResourcePolicyConfigMapName: resource-policy - # TDX configuration file - kbsTdxConfigMapName: tdx-config + # TDX settings + tdxConfigSpec: + kbsTdxConfigMapName: tdx-config-sample + # IBMSE settings + ibmSEConfigSpec: + certStorePvc: ibmse-pvc ``` ## Getting Started @@ -241,6 +256,10 @@ You’ll need a Kubernetes cluster to run against. You can use [KIND](https://si ``` It is also possible to create the K8s secrets (a commented out example is provided in the [kustomization.yaml](config/samples/microservices/kustomization.yaml)). To enable the secrets you'd need to uncomment the relevant secret generator entry and patch. + +### IBM Secure Execution + +For IBM SE specific configuration, please refer to [ibmse.md](docs/ibmse.md). ### Uninstall CRDs diff --git a/api/v1alpha1/kbsconfig_types.go b/api/v1alpha1/kbsconfig_types.go index 6fa07a4..69bbefa 100644 --- a/api/v1alpha1/kbsconfig_types.go +++ b/api/v1alpha1/kbsconfig_types.go @@ -43,6 +43,13 @@ type TdxConfigSpec struct { 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 + // +optional + CertStorePvc string `json:"certStorePvc,omitempty"` +} + // KbsConfigSpec defines the desired state of KbsConfig type KbsConfigSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster @@ -98,6 +105,10 @@ type KbsConfigSpec struct { // 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"` } // KbsConfigStatus defines the observed state of KbsConfig diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 07a22a0..c7ffdde 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -24,6 +24,21 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IbmSEConfigSpec) DeepCopyInto(out *IbmSEConfigSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IbmSEConfigSpec. +func (in *IbmSEConfigSpec) DeepCopy() *IbmSEConfigSpec { + if in == nil { + return nil + } + out := new(IbmSEConfigSpec) + 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 @@ -92,6 +107,7 @@ func (in *KbsConfigSpec) DeepCopyInto(out *KbsConfigSpec) { copy(*out, *in) } out.TdxConfigSpec = in.TdxConfigSpec + out.IbmSEConfigSpec = in.IbmSEConfigSpec } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KbsConfigSpec. diff --git a/bundle/manifests/confidentialcontainers.org_kbsconfigs.yaml b/bundle/manifests/confidentialcontainers.org_kbsconfigs.yaml index 6e51ee1..cd32bd2 100644 --- a/bundle/manifests/confidentialcontainers.org_kbsconfigs.yaml +++ b/bundle/manifests/confidentialcontainers.org_kbsconfigs.yaml @@ -39,6 +39,15 @@ spec: spec: description: KbsConfigSpec defines the desired state of KbsConfig properties: + ibmSEConfigSpec: + description: ibmSEConfigSpec is the struct that hosts the IBMSE specific + configuration + properties: + certStorePvc: + description: certStorePvc is the name of the PeristentVolumeClaim + where certificates/keys are mounted + type: string + type: object kbsAsConfigMapName: description: |- KbsAsConfigMapName is the name of the configmap that contains the KBS AS configuration diff --git a/config/crd/bases/confidentialcontainers.org_kbsconfigs.yaml b/config/crd/bases/confidentialcontainers.org_kbsconfigs.yaml index 1f038cb..73d6f30 100644 --- a/config/crd/bases/confidentialcontainers.org_kbsconfigs.yaml +++ b/config/crd/bases/confidentialcontainers.org_kbsconfigs.yaml @@ -39,6 +39,15 @@ spec: spec: description: KbsConfigSpec defines the desired state of KbsConfig properties: + ibmSEConfigSpec: + description: ibmSEConfigSpec is the struct that hosts the IBMSE specific + configuration + properties: + certStorePvc: + description: certStorePvc is the name of the PeristentVolumeClaim + where certificates/keys are mounted + type: string + type: object kbsAsConfigMapName: description: |- KbsAsConfigMapName is the name of the configmap that contains the KBS AS configuration diff --git a/config/samples/all-in-one/ibmse-pv.yaml b/config/samples/all-in-one/ibmse-pv.yaml new file mode 100644 index 0000000..23dd54f --- /dev/null +++ b/config/samples/all-in-one/ibmse-pv.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: ibmse-pv + namespace: kbs-operator-system +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 diff --git a/config/samples/all-in-one/ibmse-pvc.yaml b/config/samples/all-in-one/ibmse-pvc.yaml new file mode 100644 index 0000000..e0d20a7 --- /dev/null +++ b/config/samples/all-in-one/ibmse-pvc.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: ibmse-pvc + namespace: kbs-operator-system +spec: + accessModes: + - ReadOnlyMany + storageClassName: "" + resources: + requests: + storage: 100Mi diff --git a/config/samples/all-in-one/kustomization.yaml b/config/samples/all-in-one/kustomization.yaml index d4c37f8..20114b2 100644 --- a/config/samples/all-in-one/kustomization.yaml +++ b/config/samples/all-in-one/kustomization.yaml @@ -30,4 +30,6 @@ resources: - rvps-reference-values.yaml - resource-policy.yaml - tdx-config.yaml +- ibmse-pv.yaml +- ibmse-pvc.yaml #+kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/samples/microservices/ibmse-pv.yaml b/config/samples/microservices/ibmse-pv.yaml new file mode 100644 index 0000000..23dd54f --- /dev/null +++ b/config/samples/microservices/ibmse-pv.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: ibmse-pv + namespace: kbs-operator-system +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 diff --git a/config/samples/microservices/ibmse-pvc.yaml b/config/samples/microservices/ibmse-pvc.yaml new file mode 100644 index 0000000..e0d20a7 --- /dev/null +++ b/config/samples/microservices/ibmse-pvc.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: ibmse-pvc + namespace: kbs-operator-system +spec: + accessModes: + - ReadOnlyMany + storageClassName: "" + resources: + requests: + storage: 100Mi diff --git a/config/samples/microservices/kustomization.yaml b/config/samples/microservices/kustomization.yaml index 0ad4830..ef355c5 100644 --- a/config/samples/microservices/kustomization.yaml +++ b/config/samples/microservices/kustomization.yaml @@ -33,4 +33,6 @@ resources: - rvps-reference-values.yaml - resource-policy.yaml - tdx-config.yaml +- ibmse-pv.yaml +- ibmse-pvc.yaml #+kubebuilder:scaffold:manifestskustomizesamples diff --git a/docs/ibmse.md b/docs/ibmse.md new file mode 100644 index 0000000..04877b6 --- /dev/null +++ b/docs/ibmse.md @@ -0,0 +1,95 @@ +# IBM Secure Execution (SE) + +## Download certificate and keys + +In order to Trustee to work properly with IBM SE, a directory containing certificates and keys needs to be mounted in the trustee pod file system. +More information about the IBM download process can be found [here](https://github.com/confidential-containers/trustee/blob/main/deps/verifier/src/se/README.md#download-certs-crls). + +By the end of the aforementioned procedure, you should end up having a directory like the following: + +``` +├── certs +│ ├── ibm-z-host-key-signing-gen2.crt +| └── DigiCertCA.crt +├── crls +│ └── ibm-z-host-key-gen2.crl +│ └── DigiCertTrustedRootG4.crl +│ └── DigiCertTrustedG4CodeSigningRSA4096SHA3842021CA1.crl +├── hdr +│ └── hdr.bin +├── hkds +│ └── HKD-3931-0275D38.crt +└── rsa + ├── encrypt_key.pem + └── encrypt_key.pub +``` + +## Persistent Volume creation + +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. + +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: + +PersistentVolume: + +```yaml +apiVersion: v1 +kind: PersistentVolume +metadata: + name: ibmse-pv + namespace: kbs-operator-system +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: the `path` has to match a local directory on the worker node. + +PersistentVolumeClaim: + +```yaml +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: ibmse-pvc + namespace: kbs-operator-system +spec: + accessModes: + - ReadOnlyMany + storageClassName: "" + 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: + +```yaml +apiVersion: confidentialcontainers.org/v1alpha1 +kind: KbsConfig +metadata: + name: kbsconfig-sample + namespace: kbs-operator-system +spec: + # omitted all the rest of config + # ... + + # IBMSE settings + ibmSEConfigSpec: + certStorePvc: ibmse-pvc +``` + +The `certStorePvc` has to match the aforementioned PVC name. diff --git a/internal/controller/common.go b/internal/controller/common.go index 7122769..bce90a4 100644 --- a/internal/controller/common.go +++ b/internal/controller/common.go @@ -67,6 +67,9 @@ const ( // TDX config file tdxConfigFile = "sgx_default_qcnl.conf" + + // IBM SE path + ibmSePath = "/run/confidential-containers/ibmse/" ) func contains(list []string, s string) bool { diff --git a/internal/controller/kbsconfig_controller.go b/internal/controller/kbsconfig_controller.go index 524b4c2..672f6b3 100644 --- a/internal/controller/kbsconfig_controller.go +++ b/internal/controller/kbsconfig_controller.go @@ -380,6 +380,17 @@ func (r *KbsConfigReconciler) newKbsDeployment(ctx context.Context) (*appsv1.Dep kbsVM = append(kbsVM, volumeMount) } + // IBMSE specific configuration + if r.kbsConfig.Spec.IbmSEConfigSpec.CertStorePvc != "" { + volume, err := r.createPVCVolume(ctx, r.kbsConfig.Spec.IbmSEConfigSpec.CertStorePvc) + if err != nil { + return nil, err + } + volumeMount = createVolumeMount(volume.Name, ibmSePath) + volumes = append(volumes, *volume) + kbsVM = append(kbsVM, volumeMount) + } + // auth-secret volume, err = r.createSecretVolume(ctx, "auth-secret", r.kbsConfig.Spec.KbsAuthSecretName) if err != nil { diff --git a/internal/controller/volumes.go b/internal/controller/volumes.go index c8a36f9..45f3a75 100644 --- a/internal/controller/volumes.go +++ b/internal/controller/volumes.go @@ -132,3 +132,16 @@ func createVolumeMountWithSubpath(volumeName string, mountPath string, subPath s SubPath: subPath, } } + +func (r *KbsConfigReconciler) createPVCVolume(ctx context.Context, volumeName string) (*corev1.Volume, error) { + volume := corev1.Volume{ + Name: volumeName, + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: volumeName, + ReadOnly: true, + }, + }, + } + return &volume, nil +}