From 4a85c009824568aedae8e38a1b7b64abad32d9f1 Mon Sep 17 00:00:00 2001 From: Manohar Reddy Date: Mon, 17 Aug 2026 15:00:05 +0200 Subject: [PATCH 1/2] docs: document DHCHAP via the StoragePool CRD for Kubernetes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit simplyblock-operator PR #417 fixed DHCHAP-gated volumes so they can actually mount through the CSI driver on Kubernetes, and made the StoragePool CRD's dhchap/allowedNodes fields fully automate host NQN registration and node-scoped scheduling. Neither the CRD-driven flow nor the resulting dhchap_node_label StorageClass parameter were documented anywhere — the existing authentication-encryption page only covered the raw sbcli CLI flow. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_0191TsKdkt9Uj3VLNcrkecE9 --- .../security/authentication-encryption.md | 32 +++++++++++++++ docs/kubernetes/usage/storage-class.md | 39 ++++++++++--------- 2 files changed, 52 insertions(+), 19 deletions(-) diff --git a/docs/kubernetes/operations/security/authentication-encryption.md b/docs/kubernetes/operations/security/authentication-encryption.md index d3fedaa7..05ccb01c 100644 --- a/docs/kubernetes/operations/security/authentication-encryption.md +++ b/docs/kubernetes/operations/security/authentication-encryption.md @@ -47,3 +47,35 @@ When connecting a volume with host access control enabled, the `--host-nqn` flag For a detailed explanation of the security mechanisms and configuration, see [NVMe-oF Security](../../../architecture/concepts/nvmf-security.md). + +## Configuring DHCHAP via the StoragePool CRD + +On Kubernetes deployments managed by the simplyblock operator, DHCHAP and host access control are configured +declaratively on the `StoragePool` custom resource instead of via `{{ cliname }}` directly: + +```yaml title="Enable DHCHAP and restrict a pool to specific Kubernetes nodes" +apiVersion: storage.simplyblock.io/v1alpha1 +kind: StoragePool +metadata: + name: pool-a + namespace: simplyblock +spec: + clusterName: cluster-a + dhchap: true + allowedNodes: + - worker-1 + - worker-2 +``` + +The operator reconciles this into everything the CLI-based flow above does manually: + +- Registers each node in `allowedNodes` as an allowed host, using a deterministic NQN derived from that + node's Kubernetes UID (`nqn.2014-08.io.simplyblock:uuid:`) — no manual `--host-nqn` bookkeeping. +- Labels each allowed node and creates a StorageClass restricted to those nodes (`allowedTopologies`), so a + Pod using this pool's PersistentVolumeClaim can only ever be scheduled onto an allowed node. +- The CSI node plugin on each node automatically presents that node's own NQN and DHCHAP secret when + connecting — no `--host-nqn` needs to be supplied anywhere in the Kubernetes flow. + +`dhchap` and `allowedNodes` are immutable once set, the same as `StorageClassParameters`. See the +[Operator Reference](../../../reference/operator/reference.md) for the full `StoragePool` field list, and +[Storage Class](../../usage/storage-class.md) for the `dhchap_node_label` parameter this generates. diff --git a/docs/kubernetes/usage/storage-class.md b/docs/kubernetes/usage/storage-class.md index 4ec10b39..02e9aa17 100644 --- a/docs/kubernetes/usage/storage-class.md +++ b/docs/kubernetes/usage/storage-class.md @@ -64,22 +64,23 @@ If `namespace-volumes` is set to `yes`, the number of namespaces per subsystem h ## Available Parameters -| Parameter Name | Value Type | Description | Optional | Default | -|---------------------------|------------|-------------------------------------------------------------------------------------------------------------------------------------|----------|----------| -| cluster_id | string | Defines the backing cluster id for the storage class. Required unless `zone_cluster_map` or `region_cluster_map` is used. | true | | -| zone_cluster_map | string | JSON map of Kubernetes zone to simplyblock cluster id (for topology-aware multi-cluster provisioning). | true | | -| region_cluster_map | string | JSON map of Kubernetes region to simplyblock cluster id (for topology-aware multi-cluster provisioning). | true | | -| fabric | string | Defines the fabric type to connect to the storage cluster. Valid values are `tcp` and `rdma`. | true | `tcp` | -| csi.storage.k8s.io/fstype | string | Defines the filesystem to format the logical volume. If not specific, a raw block device is given to the container. | true | | -| pool_name | string | Defines the simplyblock storage pool name to use. | false | testing1 | -| qos_rw_iops | int | Defines the maximum IOPS reserved for a logical volume of this storage class. A zero (0) means no maximum. | true | 0 | -| qos_rw_mbytes | int | Defines the maximum total throughput in megabytes reserved for a logical volume of this storage class. A zero (0) means no maximum. | true | 0 | -| qos_r_mbytes | int | Defines the maximum read throughput in megabytes reserved for a logical volume of this storage class. A zero (0) means no maximum. | true | 0 | -| qos_w_mbytes | int | Defines the maximum write throughput in megabytes reserved for a logical volume of this storage class. A zero (0) means no maximum. | true | 0 | -| compression | bool | Defines if the logical volume of this storage class will be stored compressed or not. | true | false | -| encryption | bool | Defines if the logical volume of this storage class will be encrypted or not. | true | false | -| distr_ndcs | int | Defines the number of data chunks for the erasure coding scheme. | true | 1 | -| distr_npcs | int | Defines the number of parity chunks for the erasure coding scheme. | true | 1 | -| lvol_priority_class | int | Defines the priority class of a logical volume of this storage class. | true | 0 | -| max_namespace_per_subsys | int | Defines the number of namespaces per NVMe subsystem. | true | 1 | -| tune2fs_reserved_blocks | int | Defines the number of reserved blocks for tune2fs operations. | true | 0 | +| Parameter Name | Value Type | Description | Optional | Default | +|---------------------------|------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|----------| +| cluster_id | string | Defines the backing cluster id for the storage class. Required unless `zone_cluster_map` or `region_cluster_map` is used. | true | | +| zone_cluster_map | string | JSON map of Kubernetes zone to simplyblock cluster id (for topology-aware multi-cluster provisioning). | true | | +| region_cluster_map | string | JSON map of Kubernetes region to simplyblock cluster id (for topology-aware multi-cluster provisioning). | true | | +| fabric | string | Defines the fabric type to connect to the storage cluster. Valid values are `tcp` and `rdma`. | true | `tcp` | +| csi.storage.k8s.io/fstype | string | Defines the filesystem to format the logical volume. If not specific, a raw block device is given to the container. | true | | +| pool_name | string | Defines the simplyblock storage pool name to use. | false | testing1 | +| qos_rw_iops | int | Defines the maximum IOPS reserved for a logical volume of this storage class. A zero (0) means no maximum. | true | 0 | +| qos_rw_mbytes | int | Defines the maximum total throughput in megabytes reserved for a logical volume of this storage class. A zero (0) means no maximum. | true | 0 | +| qos_r_mbytes | int | Defines the maximum read throughput in megabytes reserved for a logical volume of this storage class. A zero (0) means no maximum. | true | 0 | +| qos_w_mbytes | int | Defines the maximum write throughput in megabytes reserved for a logical volume of this storage class. A zero (0) means no maximum. | true | 0 | +| compression | bool | Defines if the logical volume of this storage class will be stored compressed or not. | true | false | +| encryption | bool | Defines if the logical volume of this storage class will be encrypted or not. | true | false | +| distr_ndcs | int | Defines the number of data chunks for the erasure coding scheme. | true | 1 | +| distr_npcs | int | Defines the number of parity chunks for the erasure coding scheme. | true | 1 | +| lvol_priority_class | int | Defines the priority class of a logical volume of this storage class. | true | 0 | +| max_namespace_per_subsys | int | Defines the number of namespaces per NVMe subsystem. | true | 1 | +| tune2fs_reserved_blocks | int | Defines the number of reserved blocks for tune2fs operations. | true | 0 | +| dhchap_node_label | string | Node label key a DHCHAP pool's allowed nodes carry; restricts scheduling to them. Auto-populated by the operator from a `StoragePool`'s `dhchap`/`allowedNodes` fields. | true | | From 1552dde1e39e0e5f9d1a2de98bc0a0c79b8ea015 Mon Sep 17 00:00:00 2001 From: "Christoph Engelbert (noctarius)" Date: Fri, 21 Aug 2026 17:48:29 +0200 Subject: [PATCH 2/2] Fixes --- .../security/authentication-encryption.md | 39 ++++++++++-------- docs/kubernetes/usage/storage-class.md | 40 +++++++++---------- 2 files changed, 43 insertions(+), 36 deletions(-) diff --git a/docs/kubernetes/operations/security/authentication-encryption.md b/docs/kubernetes/operations/security/authentication-encryption.md index 05ccb01c..0f51eeb8 100644 --- a/docs/kubernetes/operations/security/authentication-encryption.md +++ b/docs/kubernetes/operations/security/authentication-encryption.md @@ -45,15 +45,12 @@ When connecting a volume with host access control enabled, the `--host-nqn` flag {{ cliname }} volume connect --host-nqn ``` -For a detailed explanation of the security mechanisms and configuration, see -[NVMe-oF Security](../../../architecture/concepts/nvmf-security.md). - ## Configuring DHCHAP via the StoragePool CRD -On Kubernetes deployments managed by the simplyblock operator, DHCHAP and host access control are configured -declaratively on the `StoragePool` custom resource instead of via `{{ cliname }}` directly: +On Kubernetes deployments managed by the Simplyblock Operator, DHCHAP and host access control are configured +declaratively on the `StoragePool` custom resource instead of through `{{ cliname }}`. -```yaml title="Enable DHCHAP and restrict a pool to specific Kubernetes nodes" +```yaml title="Example of a StoragePool with DHCHAP enabled for two worker nodes" apiVersion: storage.simplyblock.io/v1alpha1 kind: StoragePool metadata: @@ -67,15 +64,25 @@ spec: - worker-2 ``` -The operator reconciles this into everything the CLI-based flow above does manually: +The keys are generated as soon as `dhchap` is set, but authentication is only enforced once `allowedNodes` is +non-empty. Everything the flow above does by hand is then reconciled by the operator: + +- Each node in `allowedNodes` is registered as an allowed host of the pool, under a deterministic NQN derived + from that node's Kubernetes UID (`nqn.2014-08.io.simplyblock:uuid:`). +- Each allowed node is labeled `simplyblock.io/pool...: allowed`, and the generated + `StorageClass` is restricted to that label through `allowedTopologies`. The first `Pod` to consume a + `PersistentVolumeClaim` of this pool can therefore only be scheduled onto an allowed node. +- The same label is written into the `nodeAffinity` of the `PersistentVolume` when the volume is created, which + restricts every later scheduling decision on the already-bound volume. +- The node's own NQN and the pool's DHCHAP secrets are presented by the CSI node plugin on connect, so no + `--host-nqn` has to be supplied anywhere in the Kubernetes flow. -- Registers each node in `allowedNodes` as an allowed host, using a deterministic NQN derived from that - node's Kubernetes UID (`nqn.2014-08.io.simplyblock:uuid:`) — no manual `--host-nqn` bookkeeping. -- Labels each allowed node and creates a StorageClass restricted to those nodes (`allowedTopologies`), so a - Pod using this pool's PersistentVolumeClaim can only ever be scheduled onto an allowed node. -- The CSI node plugin on each node automatically presents that node's own NQN and DHCHAP secret when - connecting — no `--host-nqn` needs to be supplied anywhere in the Kubernetes flow. +`dhchap` is immutable, because the `parameters` and `allowedTopologies` of the generated `StorageClass` cannot +be patched in the Kubernetes API once it exists. `allowedNodes` stays mutable. Changing it relabels the nodes +and updates the pool's allowed hosts, and it never rewrites the `StorageClass`. -`dhchap` and `allowedNodes` are immutable once set, the same as `StorageClassParameters`. See the -[Operator Reference](../../../reference/operator/reference.md) for the full `StoragePool` field list, and -[Storage Class](../../usage/storage-class.md) for the `dhchap_node_label` parameter this generates. +See the [Operator Reference](../../../reference/operator/reference.md) for the full `StoragePool` field list, +and [Storage Class](../../usage/storage-class.md) for the `dhchap_node_label` parameter this generates. + +For a detailed explanation of the security mechanisms and configuration, see +[NVMe-oF Security](../../../architecture/concepts/nvmf-security.md). diff --git a/docs/kubernetes/usage/storage-class.md b/docs/kubernetes/usage/storage-class.md index 02e9aa17..b0770a6b 100644 --- a/docs/kubernetes/usage/storage-class.md +++ b/docs/kubernetes/usage/storage-class.md @@ -64,23 +64,23 @@ If `namespace-volumes` is set to `yes`, the number of namespaces per subsystem h ## Available Parameters -| Parameter Name | Value Type | Description | Optional | Default | -|---------------------------|------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|----------| -| cluster_id | string | Defines the backing cluster id for the storage class. Required unless `zone_cluster_map` or `region_cluster_map` is used. | true | | -| zone_cluster_map | string | JSON map of Kubernetes zone to simplyblock cluster id (for topology-aware multi-cluster provisioning). | true | | -| region_cluster_map | string | JSON map of Kubernetes region to simplyblock cluster id (for topology-aware multi-cluster provisioning). | true | | -| fabric | string | Defines the fabric type to connect to the storage cluster. Valid values are `tcp` and `rdma`. | true | `tcp` | -| csi.storage.k8s.io/fstype | string | Defines the filesystem to format the logical volume. If not specific, a raw block device is given to the container. | true | | -| pool_name | string | Defines the simplyblock storage pool name to use. | false | testing1 | -| qos_rw_iops | int | Defines the maximum IOPS reserved for a logical volume of this storage class. A zero (0) means no maximum. | true | 0 | -| qos_rw_mbytes | int | Defines the maximum total throughput in megabytes reserved for a logical volume of this storage class. A zero (0) means no maximum. | true | 0 | -| qos_r_mbytes | int | Defines the maximum read throughput in megabytes reserved for a logical volume of this storage class. A zero (0) means no maximum. | true | 0 | -| qos_w_mbytes | int | Defines the maximum write throughput in megabytes reserved for a logical volume of this storage class. A zero (0) means no maximum. | true | 0 | -| compression | bool | Defines if the logical volume of this storage class will be stored compressed or not. | true | false | -| encryption | bool | Defines if the logical volume of this storage class will be encrypted or not. | true | false | -| distr_ndcs | int | Defines the number of data chunks for the erasure coding scheme. | true | 1 | -| distr_npcs | int | Defines the number of parity chunks for the erasure coding scheme. | true | 1 | -| lvol_priority_class | int | Defines the priority class of a logical volume of this storage class. | true | 0 | -| max_namespace_per_subsys | int | Defines the number of namespaces per NVMe subsystem. | true | 1 | -| tune2fs_reserved_blocks | int | Defines the number of reserved blocks for tune2fs operations. | true | 0 | -| dhchap_node_label | string | Node label key a DHCHAP pool's allowed nodes carry; restricts scheduling to them. Auto-populated by the operator from a `StoragePool`'s `dhchap`/`allowedNodes` fields. | true | | +| Parameter Name | Value Type | Description | Optional | Default | +|---------------------------|------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|----------| +| cluster_id | string | Defines the backing cluster id for the storage class. Required unless `zone_cluster_map` or `region_cluster_map` is used. | true | | +| zone_cluster_map | string | JSON map of Kubernetes zone to simplyblock cluster id (for topology-aware multi-cluster provisioning). | true | | +| region_cluster_map | string | JSON map of Kubernetes region to simplyblock cluster id (for topology-aware multi-cluster provisioning). | true | | +| fabric | string | Defines the fabric type to connect to the storage cluster. Valid values are `tcp` and `rdma`. | true | `tcp` | +| csi.storage.k8s.io/fstype | string | Defines the filesystem to format the logical volume. If not specific, a raw block device is given to the container. | true | | +| pool_name | string | Defines the simplyblock storage pool name to use. | false | testing1 | +| qos_rw_iops | int | Defines the maximum IOPS reserved for a logical volume of this storage class. A zero (0) means no maximum. | true | 0 | +| qos_rw_mbytes | int | Defines the maximum total throughput in megabytes reserved for a logical volume of this storage class. A zero (0) means no maximum. | true | 0 | +| qos_r_mbytes | int | Defines the maximum read throughput in megabytes reserved for a logical volume of this storage class. A zero (0) means no maximum. | true | 0 | +| qos_w_mbytes | int | Defines the maximum write throughput in megabytes reserved for a logical volume of this storage class. A zero (0) means no maximum. | true | 0 | +| compression | bool | Defines if the logical volume of this storage class will be stored compressed or not. | true | false | +| encryption | bool | Defines if the logical volume of this storage class will be encrypted or not. | true | false | +| distr_ndcs | int | Defines the number of data chunks for the erasure coding scheme. | true | 1 | +| distr_npcs | int | Defines the number of parity chunks for the erasure coding scheme. | true | 1 | +| lvol_priority_class | int | Defines the priority class of a logical volume of this storage class. | true | 0 | +| max_namespace_per_subsys | int | Defines the number of namespaces per NVMe subsystem. | true | 1 | +| tune2fs_reserved_blocks | int | Defines the number of reserved blocks for tune2fs operations. | true | 0 | +| dhchap_node_label | string | Node label key carried by the allowed nodes of a DHCHAP pool, restricting volumes of this class to those nodes. Set by the operator from a `StoragePool`'s `dhchap` and `allowedNodes` fields. | true | |