- Status: Validated on Debian 13 with Linux 6.12.115. Debian 12 with Linux 6.1 is not supported for v1.
- Scope: Host requirements for LVM, LIO, nvmet, and the TLS-PSK data path. Client-side discovery, login, and mount remain caller-owned.
| Host | Kernel | LIO | nvmet | TLS-PSK | nvmet sessions | Verdict |
|---|---|---|---|---|---|---|
| Debian 13 genericcloud | 6.12.115+deb13.1-cloud-amd64 |
pass | pass | pass | unknown | Reference host |
| Debian 12 genericcloud | 6.1.0-54-cloud-amd64 |
pass | pass | unavailable | unknown | Unsupported for v1 |
The Debian 13 reference test ran under QEMU/KVM with 4 vCPU, 4 GiB RAM, and a loop-backed VG. The TLS test performed a real loopback NVMe/TCP TLS-PSK login and an 8 MiB write/read. The iSCSI test performed a real CHAP login and read the LIO session count.
Other kernels are not certified by this test. A host must pass the capability probe and the two data-path checks before it is added to the supported matrix.
The target requires:
CONFIG_TARGET_CORE=m
CONFIG_ISCSI_TARGET=m
CONFIG_TARGET_CORE_IBLOCK=m
CONFIG_NVME_TARGET=m
CONFIG_NVME_TARGET_TCP=m
CONFIG_NVME_TARGET_TCP_TLS=y
CONFIG_NVME_TARGET_AUTH=y
CONFIG_TLS=y
The initiator-side validation host additionally requires CONFIG_NVME_TCP_TLS=y. The probe reports that symbol as optional for a storage-only host.
The tested module set was:
target_core_mod
target_core_iblock
iscsi_target_mod
nvmet
nvmet_tcp
A host that lacks CONFIG_NVME_TARGET_TCP_TLS cannot serve the v1 NVMe-oF path because v1 requires TLS 1.3 PSK. It may still pass the basic LIO checks, but it must be rejected as a storage host.
Required runtime packages:
lvm2forlvs,vgs,pvs,lvcreate,lvextend,lvreduce, andlvremove.ktls-utilsfortlshd;tlshdmust be active and must include the.nvmekeyring.- The kernel modules listed above.
Initiator-side requirement for NVMe/TCP:
ktls-utilsand an activetlshdare required on every initiator node as well as on the storage host. Without the initiator-side handshake daemon, the kernel fails withfailed to start TLS: -3before the target can match a PSK.nvme-cliis required on initiator nodes fornvme connectandnvme check-tls-key.
Validation and test-only packages:
nvme-clifor the loopback NVMe/TCP TLS check.open-iscsifor the loopback CHAP check.targetcli-fb/rtslibfor test setup, cross-checking, and cleanup only.nvmetcliis optional; the adapter uses configfs directly.
targetcli and nvmetcli are not production dependencies. The adapters must not shell out to them for normal create, export, unexport, or delete operations.
The Stage 1 iSCSI adapter must encode these behaviors:
-
Configure an
iblockbackstore withcontrol:mkdir -p /sys/kernel/config/target/core/iblock_0/<name> printf 'udev_path=%s\n' /dev/<vg>/<lv> \ > /sys/kernel/config/target/core/iblock_0/<name>/control printf '1' > /sys/kernel/config/target/core/iblock_0/<name>/enable
Writing the informational
udev_pathattribute is not sufficient;enable=1then fails withEINVAL. The adapter uses the sharediblock_0HBA. A UUID-shaped HBA name such asiblock_<resource_uuid>is rejected by LIO withEINVAL; the per-volume identity belongs in the backstore name, target, LUN, and ACL. -
Create
tpgt_1, the network portal undernp/<address>:<port>, and the LUN underlun/lun_0. The LUN entry is a symlink to the backstore. -
Create the initiator ACL and its mapped LUN:
mkdir -p .../tpgt_1/acls/<initiator-iqn> mkdir -p .../tpgt_1/acls/<initiator-iqn>/lun_0 ln -s /sys/kernel/config/target/iscsi/<target-iqn>/tpgt_1/lun/lun_0 \ .../tpgt_1/acls/<initiator-iqn>/lun_0/<link-name> printf '0' > .../tpgt_1/acls/<initiator-iqn>/lun_0/write_protect
The target path must resolve at symlink time. Use the absolute configfs path or the exact relative path from the intended resolver context.
-
Write CHAP credentials without a trailing newline:
printf '%s' "$CHAP_USER" > .../acls/<initiator-iqn>/auth/userid printf '%s' "$CHAP_PASS" > .../acls/<initiator-iqn>/auth/password printf '1' > .../tpgt_1/attrib/authentication
LIO preserves the bytes, including a trailing newline. A newline becomes part of the CHAP name and causes
CHAP_N values do not matchat login. -
The authoritative LIO session count is:
/sys/kernel/config/target/iscsi/<target-iqn>/fabric_statistics/iscsi_instance/sessionstargetcli sessionsis a useful cross-check, but it is not the production interface.dynamic_sessionslists only dynamically generated ACL sessions and is empty for explicit ACLs; do not use it for the normal session count. -
The adapter must check the session count before any destructive path.
targetcli /iscsi deletesucceeds and tears down a target even with an active session; using it as a pre-check would silently disconnect a caller. The committed adapter returnsErrSessionActiveunlessforce=true. -
Multiple iSCSI targets may share the same IPv4 portal. Do not treat a duplicate iSCSI portal as a conflict; the nvmet port model is the shared-port case.
-
Teardown must remove every ACL mapped-LUN link before removing the ACL, then remove the LUN, portal, TPG, and target. The test script uses
targetclito remove leftover explicit ACLs because a single missed mapped-LUN link preventsrmdirof the ACL. The production adapter must implement the equivalent ordering directly.
The Stage 2 NVMe-oF adapter must encode these behaviors:
-
Create one subsystem and one namespace per volume:
mkdir -p /sys/kernel/config/nvmet/subsystems/<nqn> printf '0' > /sys/kernel/config/nvmet/subsystems/<nqn>/attr_allow_any_host mkdir -p /sys/kernel/config/nvmet/subsystems/<nqn>/namespaces/1 printf '%s' /dev/<vg>/<lv> \ > /sys/kernel/config/nvmet/subsystems/<nqn>/namespaces/1/device_path printf '1' > /sys/kernel/config/nvmet/subsystems/<nqn>/namespaces/1/enable
-
Allow the single initiator through the
hostsgroup and the subsystem'sallowed_hostssymlink. Do not enableattr_allow_any_host. -
Configure the TLS port while it is disabled and in this order:
mkdir -p /sys/kernel/config/nvmet/ports/<id> printf 'tcp' > /sys/kernel/config/nvmet/ports/<id>/addr_trtype printf 'tls1.3' > /sys/kernel/config/nvmet/ports/<id>/addr_tsas printf 'required' > /sys/kernel/config/nvmet/ports/<id>/addr_treq printf 'ipv4' > /sys/kernel/config/nvmet/ports/<id>/addr_adrfam printf '%s' <address> > /sys/kernel/config/nvmet/ports/<id>/addr_traddr printf '%s' <port> > /sys/kernel/config/nvmet/ports/<id>/addr_trsvcid ln -s /sys/kernel/config/nvmet/subsystems/<nqn> \ /sys/kernel/config/nvmet/ports/<id>/subsystems/<nqn>
Writing
addr_tsasoraddr_treqafter the address has activated the port fails withEINVAL/EPERMbecause the port is already enabled. Set the security attributes first. -
Install the PSK into the
.nvmekeyring and ensuretlshdis active. The validation flow was:PSK=$(nvme gen-tls-key --subsysnqn="$NQN") nvme check-tls-key --insert \ --hostnqn="$HOSTNQN" --subsysnqn="$NQN" \ --keydata="$PSK" --identity=1
A loopback
nvme connect -t tcp -a 127.0.0.1 -s <port> -n "$NQN" --tlscompleted and provided a working namespace. Production code must pass the PSK through the keyring or a mode-0600 file, never through a long-lived process argument. -
Mainline 6.12 exposes no nvmet
controllers/group and nodebugfs/nvmetsession view. After a connection the adapter cannot determine the session count and must returnSESSION_STATE_UNKNOWN. Delete and unexport require the auditedforce=truepath; the CSI driver must not force automatically.
Run as root on a disposable or candidate host:
./hack/phase0-probe.sh
./hack/phase0-kernel-check.sh
./hack/phase0-tls-check.sh
./hack/phase0-lio-check.shhack/phase0-probe.sh exits nonzero when a required storage-host item is missing. Build-only tools (go, protoc, buf, controller-gen) and test-only tools (nvme, iscsiadm, targetcli, nvmetcli) are reported but do not fail the storage host.
The kernel check prints the detected support:
lio=1 nvmet=1 tls_psk_port=1 tls_treq_required=1
tls_target_config=1 tls_host_config=1 tls_target_auth_config=1
nvmet_controllers_interface=0
The TLS check additionally prints tls_login=1, io_check=ok, and nvmet_session_interface=none. The LIO check prints iscsi_login=1 and lio_sessions=1.
- Reject Debian 12/6.1 storage hosts in v1. The kernel cannot create a TLS-only nvmet port.
- Treat nvmet session state as unknown on mainline 6.12. The API must expose the uncertainty and block destructive operations unless an administrator supplies
force=true. - Keep the LIO session count path in the readiness and reconciliation checks. A missing or unreadable count file is unknown, not zero.
- Keep
/var/lib/kouja/secret.keyand SQLite backups out of world-readable paths; the data-path credentials are encrypted at rest by the agent, not by the kernel.