diff --git a/hosts/skydick/DATAPOOL.md b/hosts/skydick/DATAPOOL.md index 53fcac3..0b59e19 100644 --- a/hosts/skydick/DATAPOOL.md +++ b/hosts/skydick/DATAPOOL.md @@ -513,6 +513,79 @@ After that, the user should change the password through the LDAP password-change flow if the admin set the initial value. +### Provisioning an iSCSI block volume (admin) + +iSCSI hands a client a raw block device (a ZFS zvol) instead of a shared filesystem. Unlike SMB/NFS, +**iSCSI targets are imperative state**: `services.target.enable = true` in the Nix config only installs +the restore unit `iscsi-target.service` (note: *not* `target.service`), which runs `targetctl restore` +from `/etc/target/saveconfig.json` at boot. You build the target with `targetcli` and persist it with +`saveconfig`; it is **not** declared in the repo. + +An iSCSI "account" = a target LUN + a per-initiator **ACL** (allowlists the client's initiator IQN) + +**CHAP** (username/password). Both together gate access. + +Prerequisites: + +- The client's initiator IQN, from `cat /etc/iscsi/initiatorname.iscsi` on the client (needs root). +- A name for the volume/target. Central/host-owned LUNs live under `dick/system/vm/`; + user-owned ones under `dick/users//vm/`. +- **Check pool headroom first** — a thick zvol reserves its full size immediately. On a near-full pool + use a thin/sparse volume (`-s`), which reserves nothing and consumes only real writes: + `zfs list dick` (watch `AVAIL`). + +Steps (run on skydick as root): + +```bash +# 1. Create the backing zvol. -s = thin/sparse. 16K volblocksize per pool convention. +zfs create -s -V 8T -o volblocksize=16K dick/system/vm/ + +# 2. Build the LIO target. Generate a CHAP secret (openssl is not on the box): +CHAP=$(tr -dc 'A-Za-z0-9' + +targetcli < dev=/dev/zvol/dick/system/vm/ +/backstores/block/ set attribute emulate_tpu=1 +/iscsi create $TGT +/iscsi/$TGT/tpg1/luns create /backstores/block/ +/iscsi/$TGT/tpg1/acls create $CLIENT_IQN +/iscsi/$TGT/tpg1/acls/$CLIENT_IQN set auth userid= password=$CHAP +/iscsi/$TGT/tpg1 set attribute authentication=1 generate_node_acls=0 cache_dynamic_acls=0 +/iscsi/$TGT/tpg1/portals delete ::0 3260 +/iscsi/$TGT/tpg1/portals create 10.0.1.1 3260 +saveconfig +EOF +``` + +Notes / gotchas: + +- `emulate_tpu=1` enables UNMAP so client discard/`fstrim` returns freed blocks to the thin pool. +- LIO auto-creates a default portal on the **IPv6 wildcard `[::0]:3260`**, not `0.0.0.0`. Delete + `::0 3260` *before* creating a specific `10.0.1.1 3260` portal, or the specific bind fails with + `EINVAL` (the wildcard already holds the port). +- `generate_node_acls=0` + `authentication=1` means only the listed IQN, with the correct CHAP, can + attach. Firewall port 3260 is open with no source restriction, so CHAP + the IQN ACL are the guard. +- Verify: `targetcli ls /iscsi` and `ss -ltnp | grep 3260`. + +Client side (open-iscsi, run as root on the client): + +```bash +TGT=iqn.2026-07.top.skyw.skydick: +iscsiadm -m discovery -t sendtargets -p 10.0.1.1 +iscsiadm -m node -T $TGT -p 10.0.1.1 --op update -n node.session.auth.authmethod -v CHAP +iscsiadm -m node -T $TGT -p 10.0.1.1 --op update -n node.session.auth.username -v +iscsiadm -m node -T $TGT -p 10.0.1.1 --op update -n node.session.auth.password -v +iscsiadm -m node -T $TGT -p 10.0.1.1 --login +iscsiadm -m node -T $TGT -p 10.0.1.1 --op update -n node.startup -v automatic # reconnect on boot +``` + +A new block device (`/dev/sdX`) of the LUN size appears; format/LVM it as normal. Mount with `discard` +or run periodic `fstrim` so deletes flow back to the thin pool. + +First target provisioned this way: `dick/system/vm/rh1288v3` → `RH1288V3-CPUserver` (10.0.200.11), +2026-07-14. + ## Quotas When a user's `dick/users/` dataset exists, its ZFS quota (default 10TB in the examples