iSCSI Target (Linux LIO) on Ubuntu 24.04 on Azure User Guide
Overview
This guide covers the deployment and configuration of iSCSI Target (Linux LIO) on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. iSCSI lets you export raw block devices — LUNs — over ordinary TCP/IP, so any standards compliant initiator (Linux, Windows, VMware, or a hypervisor) can log in and use the LUN exactly as if it were a locally attached disk. This turns a single VM into a software defined SAN.
The image is built on the Linux kernel LIO subsystem (the in kernel SCSI target engine, target_core_mod) and administered with targetcli from the targetcli-fb toolkit. The demo target is a 1 GiB fileio backstore exported as a single LUN behind the IQN iqn.2025-01.co.uk.cloudimg:iscsi-demo on a portal listening on TCP 3260. Unattended security upgrades keep the base OS patched.
Secure by design — no default credentials, no target until first boot. A block storage target that anyone can log into is a data breach waiting to happen, so this image never ships a fixed or shared credential and exports no target in the captured image. On the very first boot of every VM the demo backstore, target, LUN and portal are built, one way CHAP authentication is enabled on the portal group, and a fresh per instance CHAP username and 16 character secret are generated and written to /root/iscsi-target-credentials.txt (mode 0600, root only). An initiator that presents the wrong secret is refused; only the correct per VM secret establishes a session.
Persistent across reboots. The kernel LIO configuration is saved to /etc/rtslib-fb-target/saveconfig.json and restored by rtslib-fb-targetctl.service on every boot, so your target, LUN and access control survive reboots.
What is included:
-
The Linux kernel LIO iSCSI target, administered with
targetcli(targetcli-fb,python3-rtslib-fb) -
A 1 GiB fileio backstore exported as a LUN behind
iqn.2025-01.co.uk.cloudimg:iscsi-demoon a portal on TCP 3260 -
Mandatory one way CHAP authentication on the target portal group, so an initiator with the wrong secret is refused
-
A per instance CHAP username and secret generated on first boot, documented in
/root/iscsi-target-credentials.txt(0600 root only) -
Persistent configuration (
saveconfig.jsonrestored byrtslib-fb-targetctl.service) and theopen-iscsiinitiator tools for local verification
Prerequisites
-
Active Azure subscription, SSH public key, VNet + subnet in target region
-
Subscription to the iSCSI Target listing on Azure Marketplace
-
Network Security Group rules allowing TCP 22 (admin) and, for the initiator hosts that will use the storage, TCP 3260 (iSCSI) from your client networks
Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) for a demo or light target. Production SANs with larger LUNs and many initiators should use Standard_D2s_v5 or larger and attach dedicated data disks for the backstores.
Step 1: Deploy from the Azure Portal
Search iSCSI Target in Marketplace, select the cloudimg publisher, and click Create. Configure the Network Security Group to allow TCP 3260 from your initiator hosts, and TCP 22 for administration.
Step 2: Deploy from the Azure CLI
RG="iscsi-prod"; LOCATION="eastus"; VM_NAME="iscsi1"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/iscsi-target-lio-ubuntu-24-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az vm create \
--resource-group "$RG" --name "$VM_NAME" \
--image "$GALLERY_IMAGE_ID" \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values "$SSH_KEY" \
--public-ip-sku Standard
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 3260 --priority 1001
SSH in as azureuser once the VM is running.
Step 3: Retrieve your per-VM CHAP credentials
Every VM generates its own CHAP username and secret on first boot. They are written to a root only file, along with the target IQN and portal:
sudo grep -E '^CHAP_USERNAME=|^TARGET_IQN=|^PORTAL=' /root/iscsi-target-credentials.txt
The full file (including the CHAP secret) is readable only by root:
CHAP_USERNAME=chap_1d87eb66
TARGET_IQN=iqn.2025-01.co.uk.cloudimg:iscsi-demo
PORTAL=10.0.0.27:3260
The PORTAL value is the VM's primary IP. Initiators inside the same VNet connect on that address; internet clients connect on the VM's public IP on TCP 3260 (open it in the NSG first).
Step 4: Inspect the iSCSI target
The demo target, LUN, portal and backstore are already live. View the object tree with targetcli and confirm the portal is listening on TCP 3260:
sudo targetcli ls /iscsi
sudo ss -tlnp | grep ':3260 '
Expected output:
o- iscsi ...................................................... [Targets: 1]
o- iqn.2025-01.co.uk.cloudimg:iscsi-demo ....................... [TPGs: 1]
o- tpg1 ............................... [gen-acls, tpg-auth, 1-way auth]
o- acls .................................................... [ACLs: 0]
o- luns .................................................... [LUNs: 1]
| o- lun0 ....... [fileio/demo (/var/lib/iscsi-demo/demo.img) (default_tg_pt_gp)]
o- portals .............................................. [Portals: 1]
o- 0.0.0.0:3260 ............................................... [OK]
LISTEN 0.0.0.0:3260
The tpg-auth, 1-way auth flags confirm CHAP is enforced at the portal group level.

Step 5: Verify a real iSCSI session locally
You can prove the target end to end from the VM itself using the bundled open-iscsi initiator over the loopback address. A wrong CHAP secret is refused, while the correct per VM secret establishes a real session and a new block device appears:
sudo systemctl start iscsid open-iscsi
IQN=iqn.2025-01.co.uk.cloudimg:iscsi-demo
sudo iscsiadm -m discovery -t sendtargets -p 127.0.0.1:3260
sudo iscsiadm -m node -T "$IQN" -p 127.0.0.1:3260 --op update -n node.session.auth.authmethod -v CHAP
sudo iscsiadm -m node -T "$IQN" -p 127.0.0.1:3260 --op update -n node.session.auth.username -v <CHAP_USERNAME>
sudo iscsiadm -m node -T "$IQN" -p 127.0.0.1:3260 --op update -n node.session.auth.password -v <CHAP_PASSWORD>
sudo iscsiadm -m node -T "$IQN" -p 127.0.0.1:3260 --login
sudo iscsiadm -m session
ls -l /dev/disk/by-path/ | grep iscsi
sudo iscsiadm -m node -T "$IQN" -p 127.0.0.1:3260 --logout
Expected output (a live session and a new /dev/sd* device backed by the LUN):
127.0.0.1:3260,1 iqn.2025-01.co.uk.cloudimg:iscsi-demo
Login to [iface: default, target: iqn.2025-01.co.uk.cloudimg:iscsi-demo, portal: 127.0.0.1,3260] successful.
tcp: [1] 127.0.0.1:3260,1 iqn.2025-01.co.uk.cloudimg:iscsi-demo (non-flash)
ip-127.0.0.1:3260-iscsi-iqn.2025-01.co.uk.cloudimg:iscsi-demo-lun-0 -> ../../sdc
Logout of [iface: default, target: iqn.2025-01.co.uk.cloudimg:iscsi-demo, portal: 127.0.0.1,3260] successful.

Step 6: Connect from a remote initiator
On a remote Linux host with open-iscsi installed, point the same commands at the target VM's IP (use the private IP inside the VNet, or the public IP with TCP 3260 open in the NSG). Substitute the CHAP username and secret from Step 3:
sudo iscsiadm -m discovery -t sendtargets -p <TARGET_IP>:3260
sudo iscsiadm -m node -T iqn.2025-01.co.uk.cloudimg:iscsi-demo -p <TARGET_IP>:3260 \
--op update -n node.session.auth.authmethod -v CHAP
sudo iscsiadm -m node -T iqn.2025-01.co.uk.cloudimg:iscsi-demo -p <TARGET_IP>:3260 \
--op update -n node.session.auth.username -v <your-chap-username>
sudo iscsiadm -m node -T iqn.2025-01.co.uk.cloudimg:iscsi-demo -p <TARGET_IP>:3260 \
--op update -n node.session.auth.password -v <your-chap-secret>
sudo iscsiadm -m node -T iqn.2025-01.co.uk.cloudimg:iscsi-demo -p <TARGET_IP>:3260 --login
On Windows, use the built in iSCSI Initiator control panel: add the target VM's IP under Discovery, then under Targets connect with Advanced → CHAP log on using the username and secret from Step 3.
Step 7: Security — CHAP and no default credentials
The credentials file is 0600 root:root, and the CHAP secret is unique to this VM — it is generated on first boot and never shipped in the image:
sudo stat -c 'file: %n perms: %a owner: %U:%G' /root/iscsi-target-credentials.txt
sudo grep -E '^CHAP_USERNAME=' /root/iscsi-target-credentials.txt
Expected output (the secret itself stays root only):
file: /root/iscsi-target-credentials.txt perms: 600 owner: root:root
CHAP_USERNAME=chap_1d87eb66
Because the captured image exports no target at all until first boot, there is no window in which a default CHAP secret is live. Each VM builds its own target with its own secret.

Step 8: Persistence and customizing your target
The configuration is persisted to /etc/rtslib-fb-target/saveconfig.json and restored on every boot by rtslib-fb-targetctl.service:
sudo systemctl is-enabled rtslib-fb-targetctl.service
sudo python3 -c "import json;d=json.load(open('/etc/rtslib-fb-target/saveconfig.json'));print('targets:',len(d['targets']),'storage objects:',len(d['storage_objects']))"
Expected output:
enabled
targets: 1 storage objects: 1
To add your own LUN — for example a larger fileio backstore, or a block backed LUN on a dedicated Azure data disk — use targetcli and then sudo targetctl save to persist. This is run on the target VM (illustrative):
# a larger file backed LUN
sudo targetcli /backstores/fileio create name=data1 file_or_dev=/var/lib/iscsi-demo/data1.img size=50G
sudo targetcli /iscsi/iqn.2025-01.co.uk.cloudimg:iscsi-demo/tpg1/luns create /backstores/fileio/data1
# or a block backed LUN on an attached data disk (/dev/sdX)
sudo targetcli /backstores/block create name=disk1 dev=/dev/disk/azure/scsi1/lun0
sudo targetcli /iscsi/iqn.2025-01.co.uk.cloudimg:iscsi-demo/tpg1/luns create /backstores/block/disk1
sudo targetctl save

Managing the service
sudo systemctl status rtslib-fb-targetctl.service --no-pager
sudo targetcli ls /iscsi
- Reload the persisted config after manual edits:
sudo targetctl restore - Persist live changes:
sudo targetctl save - The LIO target runs in the kernel; there is no user space daemon to restart
Troubleshooting
- Initiator cannot discover the target: confirm TCP 3260 is open in the NSG from the client, and that
sudo ss -tlnp | grep ':3260 'shows a LISTEN socket on the VM. - Login refused: the CHAP username or secret is wrong. Re read them from
/root/iscsi-target-credentials.txt(Step 3). CHAP is mandatory — anonymous logins are always refused. - No new block device after login: run
sudo iscsiadm -m sessionto confirm the session is up, then checkls -l /dev/disk/by-path/ | grep iscsi.
Support
Every cloudimg deployment is backed by 24/7 support. For assistance, contact support@cloudimg.co.uk or visit www.cloudimg.co.uk.