It
Storage Azure

iSCSI Target (Linux LIO) on Ubuntu 24.04 on Azure User Guide

| Product: iSCSI Target (Linux LIO) on Ubuntu 24.04 LTS on Azure

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-demo on 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.json restored by rtslib-fb-targetctl.service) and the open-iscsi initiator 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.

The iSCSI target object tree shown by targetcli: a single target IQN with one TPG flagged gen-acls, tpg-auth and 1-way auth, a 1 GiB fileio backstore exported as lun0, and a portal listening on 0.0.0.0 port 3260, with ss confirming the LISTEN socket on TCP 3260

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.

The end to end iSCSI proof over loopback: sendtargets discovery returns the target IQN, the CHAP secret is applied to the node record, login succeeds and iscsiadm shows an active session, and a new block device appears under /dev/disk/by-path pointing at the LUN, then a clean logout

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.

The security posture: the per VM credentials file is mode 600 owned by root, holding a CHAP username and a 16 character secret that is generated uniquely on each VM first boot, so the image ships no shared or default credential and exports no target until first boot completes

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

The persistence proof: rtslib-fb-targetctl.service is enabled so the LIO configuration is restored on every boot, and the saved configuration file records one target and one storage object, confirming the demo LUN survives reboots

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 session to confirm the session is up, then check ls -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.