iSCSI CHAP 认证是块存储接入的第一道闸门。iSCSI 把 SCSI 命令封装进 TCP 明文传输,默认只靠 IQN 名称识别发起端,任何人都能用 iscsiadm 扫描到 target 并尝试登录。一旦存储网段与办公网、生产网未隔离,攻击者拿到一台普通主机即可挂载后端 LUN,直接读取数据库文件、虚拟机镜像与备份卷。本文按「网络隔离 → 双向 CHAP → LUN 掩码」三层给出可复现的加固配置。
适用场景
- 自建 iSCSI 存储(LIO/targetcli、TrueNAS、群晖、华为/浪潮 IPSAN)为数据库、虚拟化或备份提供块设备。
- 存储网段与业务网段共用交换机,未做独立 VLAN,存在被内网主机探测的风险。
- 等保测评中「访问控制」「身份鉴别」条款要求存储设备启用双向认证与最小授权。
- 需要限制某台 initiator 只能看到指定 LUN,而不是登录后看到全部映射卷。
前置条件
- Target 端:Linux(CentOS/RHEL/Ubuntu 均可),已安装
targetcli(包名targetcli-fb或targetcli),后端盘为独立分区或 LVM 逻辑卷,不要直接用整块系统盘。 - Initiator 端:安装
iscsi-initiator-utils(RHEL 系)或open-iscsi(Debian 系)。 - 两端已规划独立存储网段,例如
10.10.20.0/24,且能互相 ping 通 3260 端口。 - 具备 root 权限,且已确认 LUN 上的数据可以清空(首次建仓会写坏原有文件系统)。
原理说明
iSCSI 会话分两个阶段:发现阶段(Discovery)与登录阶段(Login)。发现阶段访问 target 的 3260 端口,通过 SendTargets 拿到可用的 IQN 列表;登录阶段才真正建立会话并挂载 LUN。很多加固只给登录会话加了 CHAP,发现阶段仍然匿名可查,攻击者能枚举出全部 target 名称——这属于信息泄露,必须两个阶段都启用认证。
CHAP 分单向与双向:单向只由 target 校验 initiator 的口令;双向(mutual)则 initiator 也要反过来校验 target,防止攻击者伪造 target 骗取 initiator 的口令摘要。生产环境应启用双向认证。
LUN 掩码(LUN Masking)解决的是授权粒度问题:即使认证通过,也只把指定 LUN 映射给指定 ACL 条目,其余 LUN 对该 initiator 不可见。认证解决「你是谁」,掩码解决「你能看什么」,两者缺一不可。需要说明的是,CHAP 口令以明文摘要参与握手,链路本身不加密,所以存储网段必须独立隔离,不能依赖 CHAP 抵御链路嗅探。
操作步骤
1. 划分存储网段并收紧防火墙
为存储网卡单独配置地址,禁止该网卡承担默认路由。以 ens224 为例:
# 存储专用网卡配置(NetworkManager)
nmcli con add type ethernet ifname ens224 con-name iscsi-net \
ipv4.method manual ipv4.addresses 10.10.20.10/24 \
ipv4.never-default yes ipv6.method disabled
nmcli con up iscsi-net
# 只允许指定 initiator 访问 3260,其余一律拒绝
firewall-cmd --permanent --new-zone=iscsi-target
firewall-cmd --permanent --zone=iscsi-target --add-source=10.10.20.21/32
firewall-cmd --permanent --zone=iscsi-target --add-port=3260/tcp
firewall-cmd --permanent --zone=iscsi-target --set-target=DROP
firewall-cmd --reload
firewall-cmd --zone=iscsi-target --list-all
关键点:--set-target=DROP 让未命中规则的包静默丢弃,避免暴露端口存在性;ipv4.never-default yes 防止存储流量走公网出口。
2. 创建后端存储与 target IQN
# 假定 /dev/vg_data/lv_db 为已准备好的 100G 逻辑卷
targetcli /backstores/block create name=db_lun dev=/dev/vg_data/lv_db
# IQN 命名建议带上公司域名反写与用途,便于审计
targetcli /iscsi create iqn.2026-10.cn.example:storage.db
# 绑定监听地址,不要监听 0.0.0.0
targetcli /iscsi/iqn.2026-10.cn.example:storage.db/portals create 10.10.20.10
3. 为 initiator 建立 ACL 并启用 CHAP
ACL 条目名称必须与 initiator 的 /etc/iscsi/initiatorname.iscsi 完全一致,否则登录会被拒绝。CHAP 口令长度受协议限制,通常要求 12 到 16 个字符,过长会导致握手失败。
TGT=iqn.2026-10.cn.example:storage.db
INI=iqn.2026-10.cn.example:client.db01
targetcli /iscsi/$TGT/tpg1/acls create $INI
# 开启双向 CHAP:authentication=1,并分别设置两个方向的口令
targetcli /iscsi/$TGT/tpg1/acls/$INI set attribute authentication=1
targetcli /iscsi/$TGT/tpg1/acls/$INI set auth userid=db01init
targetcli /iscsi/$TGT/tpg1/acls/$INI set auth password=InitPass2026A
targetcli /iscsi/$TGT/tpg1/acls/$INI set auth mutual_userid=db01tgt
targetcli /iscsi/$TGT/tpg1/acls/$INI set auth mutual_password=TgtPass2026A
4. 只把指定 LUN 映射给该 ACL(LUN 掩码)
常见错误是在 tpg 级别建 lun,再给多个 ACL 授权,结果一台主机能看到全部卷。正确做法是把 LUN 建在 ACL 之下,形成一对一映射:
targetcli /iscsi/$TGT/tpg1/acls/$INI create db_lun
# 关闭该 target 的演示模式,避免任意 initiator 匿名读写
targetcli /iscsi/$TGT/tpg1 set attribute demo_mode_write_protect=0
targetcli /iscsi/$TGT/tpg1 set attribute generate_node_acls=0
# 持久化配置(默认保存到 /etc/target/saveconfig.json)
targetcli saveconfig
systemctl enable --now target
generate_node_acls=0 是必须项:它关闭「任意 initiator 自动生成 ACL」的默认行为,否则掩码形同虚设。
5. Initiator 端配置并登录
Debian/Ubuntu 下 CHAP 配置写在 /etc/iscsi/iscsid.conf;RHEL 系同样生效。两步认证分别为发现与登录配置:
cat >> /etc/iscsi/iscsid.conf <<'EOF'
# 登录会话:双向 CHAP
node.session.auth.authmethod = CHAP
node.session.auth.username = db01init
node.session.auth.password = InitPass2026A
node.session.auth.username_in = db01tgt
node.session.auth.password_in = TgtPass2026A
# 发现会话:同样启用 CHAP,防止 target 被匿名枚举
discovery.sendtargets.auth.authmethod = CHAP
discovery.sendtargets.auth.username = db01init
discovery.sendtargets.auth.password = InitPass2026A
discovery.sendtargets.auth.username_in = db01tgt
discovery.sendtargets.auth.password_in = TgtPass2026A
EOF
systemctl restart iscsid
iscsiadm -m discovery -t sendtargets -p 10.10.20.10
iscsiadm -m node -T $TGT -p 10.10.20.10 --login
iscsiadm -m node -T $TGT -p 10.10.20.10 --op update -n node.startup -v automatic
6. 权限控制与开机自启顺序
systemctl is-enabled iscsid
systemctl enable iscsid
# 首次识别到新盘后查看设备名
lsblk -S | grep -i iscsi
# 建议用 /dev/disk/by-path 固定设备名,避免 /dev/sdb 漂移
ls -l /dev/disk/by-path/ | grep iscsi
配置验证
验证要同时检查「授权主机能进」与「未授权主机进不来」两件事:
# Target 端:确认 ACL、CHAP 属性与 LUN 归属
targetcli ls /iscsi/$TGT/tpg1/acls
# Initiator 端:查看会话详情,确认 auth 为 CHAP 且状态 LOGGED_IN
iscsiadm -m session -P 3 | grep -Ei 'auth|state|Target'
# 连接数与对端地址
ss -tnp state established '( dport = :3260 )'
# 反向测试:从一台未配置 ACL 的主机执行发现,应当失败
iscsiadm -m discovery -t sendtargets -p 10.10.20.10
# 预期输出:iscsiadm: Login failed to authenticate with target
再故意把 initiator 的 node.session.auth.password 改错一位,执行 iscsiadm --login,预期报 CHAP authentication failed;dmesg 与 journalctl -u target 会同时记录失败日志。这一步能证明认证链路真实生效,而不是被演示模式放行。
常见问题
FAQ 1:登录阶段认证成功,但发现阶段报认证失败怎么办?
这是 discovery 与 node 两套配置不同步导致的。发现会话读取的是 discovery.sendtargets.auth.* 四行,登录会话读取的是 node.session.auth.*,只改后者时 iscsiadm -m discovery 会直接失败。两套必须同时补齐,并注意 username_in/password_in 只在启用双向 CHAP 时才需要填写,单向认证时留空即可,否则会因多传字段被 target 拒绝。
FAQ 2:同一台主机出现两块重复的 LUN,怎么处理?
多路径或多网卡场景下,initiator 会为同一条 target 建多条会话,扫描时就会看到 /dev/sdb 与 /dev/sdc 两份相同数据。正确做法是用 multipathd 聚合,而不是直接格式化其中一块。检查 multipath -ll 是否已识别为同一 WWID;若不需要多路径,在 iscsid.conf 中把 node.session.nr_sessions 设为 1,并在 target 侧只保留一个 portal。
FAQ 3:修改 CHAP 口令后旧会话仍可读写?
CHAP 只在建立会话时校验,已建立的会话不会因为口令变更而中断。改完口令必须双向生效:target 端 targetcli set auth 后执行 saveconfig 并 systemctl restart target;initiator 端更新 iscsid.conf 后执行 iscsiadm -m node -u 登出再用 --login 重新登录,否则旧会话会一直保持到重启。
总结
iSCSI 加固的核心是把「网络可达」变成「网络不可达,认证后才可达」:存储网段独立并默认丢弃、双向 CHAP 覆盖发现与登录两个阶段、LUN 建在 ACL 之下实现一对一掩码、关闭 generate_node_acls 与演示模式。四条全部落地后,即使攻击者拿到同网段的一台普通主机,也只能看到 3260 端口被丢弃,无法枚举 IQN,更无法挂载卷。配置完成后建议把 targetcli ls 的输出与 /etc/target/saveconfig.json 纳入配置基线,任何变更都走审批与回归验证。