亲手做 SELinux 与 firewalld — 以及用户、ACL、sshd、dnf
目标
在 AlmaLinux 9 VM 上真正设置 SELinux 布尔值、文件上下文和端口上下文,亲手操作 firewalld 的 runtime/permanent 以及 zone 和 rich rule,并留下结果地配置用户、sudoers、profile.d、limits、ACL、sshd drop-in 和 dnf 锁定。
为什么重要
LFCS 的 Operations 领域明确要求“用 SELinux 管理访问”,Networking 领域则要求数据包过滤和 OpenSSH。Pod 中既没有 SELinux 也没有 firewalld,所以这些内容只在模拟考试中一带而过。这台 VM 处于 Enforcing 状态,并且 firewalld 正在运行,所以如果漏掉端口上下文,httpd 就真的起不来;如果漏掉 permanent,一执行 reload 规则就会消失。
有三点会反复出现。第一,当前值和永久值存放在不同的位置——setsebool 的 -P、chcon 与 semanage fcontext、firewalld 的 runtime 与 permanent。第二,生效值不是由文件,而是由工具告知——sshd -T、getsebool、sudo -l -U、getfacl。第三,为避免把自己锁在外面,先做检查——visudo -cf、sshd -t、apachectl configtest。
⚠️ 评分代理通过 8899/tcp 进入。操作 firewalld 时,如果删除这个端口或把默认 zone 改成 drop,评分就无法到达,从下一步开始会出现找不出原因的失败。会话从 60 分钟开始,最长可延长到 180 分钟,会话结束后 VM 会消失。
步骤
- 把
sestatus的输出保存到/root/lfcs/selinux/status.txt(必须是 Enforcing)。然后开启布尔值httpd_can_network_connect,并让它在重启后仍然保持。 - 在
/srv/web/index.html中写入selinux ok,让/srv/web以下永久具有httpd_sys_content_t上下文,然后应用(semanage fcontext + restorecon)。把 tcp 8081 注册为http_port_t,并通过/etc/httpd/conf.d/lfcs.conf让 httpd 在 8081 上提供/srv/web,再 enable、start。curl http://127.0.0.1:8081/必须返回selinux ok。 - 在默认 zone(public)中,同时在 runtime 和 permanent 两侧开放服务
http和端口8081/tcp。评分端口8899/tcp必须保持不变。最后把firewall-cmd --list-all的输出保存到/root/lfcs/firewall/public.txt。 - 新建 zone
lab,并把来源198.51.100.0/24分配给该 zone。向labzone 中加入服务ssh,以及允许来自198.51.100.0/24的 tcp 8081 的 rich rule(rule family="ipv4" source address="198.51.100.0/24" port port="8081" protocol="tcp" accept)。全部以 permanent 方式写入并 reload,使其在 runtime 中也能看到。 - 创建组
ops,创建用户deploy(有主目录,shell 为/bin/bash),并把它作为附加组加入ops。在/etc/sudoers.d/ops中写一条规则,允许ops组无需密码、只能以 sudo 运行/usr/bin/systemctl,并把权限设为0440。用visudo -cf检查时,语法必须通过。 - 在
/etc/profile.d/lfcs.sh中把环境变量LFCS_ENV设为lab并 export。在/etc/security/limits.d/90-lfcs.conf中,写入用户deploy的打开文件数(nofile)hard 限制为4096,以及组ops的进程数(nproc)soft 限制为2048。用su - deploy打开登录 shell 时,env中必须能看到该变量,且ulimit -Hn必须为 4096。 - 创建目录
/srv/web/upload,所有者为 root,权限为750(不要用 chmod 放宽),通过 ACL 给用户deploy授予rwx,并设置默认(default)ACL,使该目录中新建的文件继承组ops的r-x。把getfacl /srv/web/upload的输出保存到/root/lfcs/acl/upload.acl。(设置 ACL 之后,ls -ld中组的位置会显示为 mask(rwx)——这是正常的。) - 在
/etc/ssh/sshd_config.d/50-lfcs.conf这个 drop-in 中写入MaxAuthTries 3、ClientAliveInterval 300、PermitRootLogin no,并重启 sshd。用sshd -T看到的最终值,三项都必须是这些值。 - 安装
tree软件包,并使其排除在升级之外:用 versionlock 锁定,或把它加入/etc/dnf/dnf.conf的exclude=。把dnf history list的输出保存到/root/lfcs/pkg/history.txt,把rpm -q tree的输出保存到/root/lfcs/pkg/tree.txt。
参考
- SELinux:
getenforce、setsebool -P、semanage fcontext -a -t … '/경로(/.*)?'(占位符为路径)→restorecon -Rv、semanage port -a -t http_port_t -p tcp 8081。拒绝记录用ausearch -m avc -ts recent。 - firewalld:用
--permanent写入,再--reload。用--zone=lab --list-all可以完整查看一个 zone。 - 用户:
useradd -m -s /bin/bash -G ops deploy、visudo -cf /etc/sudoers.d/ops、sudo -l -U deploy。 - limits 在登录的瞬间生效。在已经打开的 shell 中查看
ulimit是不会变化的。 - 常见错误 1:只用
chcon修改上下文就结束——执行一次restorecon就会恢复原样。 - 常见错误 2:只把 firewalld 规则放进 runtime,然后
--reload把它清掉(reload 会重新读取 permanent)。 - 常见错误 3:在 profile.d 中漏掉
export,导致只剩下 shell 变量。
确认 SELinux 模式与布尔值
把 sestatus 的输出保存到 /root/lfcs/selinux/status.txt(必须是 Enforcing)。然后开启布尔值 httpd_can_network_connect,并让它在重启后仍然保持。
给 setsebool 加上 -P,就会写入策略存储。用 getsebool 和 semanage boolean -l -C(只查看本地修改)确认。
用文件上下文和端口上下文让 httpd 运行在 8081
在 /srv/web/index.html 中写入 selinux ok,让 /srv/web 以下永久具有 httpd_sys_content_t 上下文,然后应用(semanage fcontext + restorecon)。把 tcp 8081 注册为 http_port_t,并通过 /etc/httpd/conf.d/lfcs.conf 让 httpd 在 8081 上提供 /srv/web,再 enable、start。curl http://127.0.0.1:8081/ 必须返回 selinux ok。
chcon 的修改在 restorecon 之后就会消失——semanage fcontext -a -t <类型> '/srv/web(/.*)?' 才是永久的。如果不注册端口,httpd 就无法绑定 8081,启动本身就会失败(用 journalctl -u httpd 和 ausearch -m avc 确认)。
firewalld 的服务与端口——runtime 与 permanent
在默认 zone(public)中,同时在 runtime 和 permanent 两侧开放服务 http 和端口 8081/tcp。评分端口 8899/tcp 必须保持不变。最后把 firewall-cmd --list-all 的输出保存到 /root/lfcs/firewall/public.txt。
加上 --permanent 只作用于文件,不加则只作用于当前正在运行的规则。两侧都要做,或者用 --permanent 写入再 --reload。用 --list-services / --permanent --list-services 确认。
新 zone 与 rich rule
新建 zone lab,并把来源 198.51.100.0/24 分配给该 zone。向 lab zone 中加入服务 ssh,以及允许来自 198.51.100.0/24 的 tcp 8081 的 rich rule(rule family="ipv4" source address="198.51.100.0/24" port port="8081" protocol="tcp" accept)。全部以 permanent 方式写入并 reload,使其在 runtime 中也能看到。
新 zone 只能用 --permanent --new-zone=lab 创建,reload 之后才会出现在 runtime 中。--zone=lab --add-source=...、--add-service=...、--add-rich-rule='...'。用 --zone=lab --list-all 确认。
组、用户与 sudoers 规则
创建组 ops,创建用户 deploy(有主目录,shell 为 /bin/bash),并把它作为附加组加入 ops。在 /etc/sudoers.d/ops 中写一条规则,允许 ops 组无需密码、只能以 sudo 运行 /usr/bin/systemctl,并把权限设为 0440。用 visudo -cf 检查时,语法必须通过。
useradd -m -s /bin/bash -G ops deploy。sudoers 的一行:%ops ALL=(ALL) NOPASSWD: /usr/bin/systemctl。语法错误会把整个 sudo 锁死,所以一定要用 visudo -cf 检查。用 sudo -l -U deploy 查看结果。
登录环境与资源限制
在 /etc/profile.d/lfcs.sh 中把环境变量 LFCS_ENV 设为 lab 并 export。在 /etc/security/limits.d/90-lfcs.conf 中,写入用户 deploy 的打开文件数(nofile)hard 限制为 4096,以及组 ops 的进程数(nproc)soft 限制为 2048。用 su - deploy 打开登录 shell 时,env 中必须能看到该变量,且 ulimit -Hn 必须为 4096。
profile.d 由登录 shell 在经过 /etc/profile 时读取。没有 export 的话,子进程(env)看不到它。limits 的一行有“对象类型 项目 值”四个字段,组写作 @ops。pam_limits 在登录时应用它。
用 ACL 授予所有权之外的权限
创建目录 /srv/web/upload,所有者为 root,权限为 750(不要用 chmod 放宽),通过 ACL 给用户 deploy 授予 rwx,并设置默认(default)ACL,使该目录中新建的文件继承组 ops 的 r-x。把 getfacl /srv/web/upload 的输出保存到 /root/lfcs/acl/upload.acl。(设置 ACL 之后,ls -ld 中组的位置会显示为 mask(rwx)——这是正常的。)
setfacl -m u:deploy:rwx <目录>,默认 ACL 用 setfacl -d -m g:ops:rx(或 -m d:g:ops:rx)。如果 ls -l 中出现 +,就说明有 ACL。mask 这一行会裁剪生效的权限。
sshd drop-in 与 sshd -T
在 /etc/ssh/sshd_config.d/50-lfcs.conf 这个 drop-in 中写入 MaxAuthTries 3、ClientAliveInterval 300、PermitRootLogin no,并重启 sshd。用 sshd -T 看到的最终值,三项都必须是这些值。
sshd_config 的 Include 在最上面,所以 drop-in 会比正文先被读取,而 sshd 使用同一个键的第一个值。用 sshd -t 检查语法,用 sshd -T 查看生效值。控制台登录与 sshd 无关,所以不用担心被锁在外面。
dnf 的安装、锁定与历史
安装 tree 软件包,并使其排除在升级之外:用 versionlock 锁定,或把它加入 /etc/dnf/dnf.conf 的 exclude=。把 dnf history list 的输出保存到 /root/lfcs/pkg/history.txt,把 rpm -q tree 的输出保存到 /root/lfcs/pkg/tree.txt。
versionlock 插件已经安装:dnf versionlock add tree / dnf versionlock list。exclude 是 dnf.conf 的 [main] 节中的 exclude=tree。history 以事务为单位,所以安装只显示为一行。