ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

嵌入式Linux安全加固实战:裁剪、权限、审计与防火墙

嵌入式Linux安全加固实战:裁剪、权限、审计与防火墙 1. 开篇为什么嵌入式 Linux 必须谈“安全加固”做嵌入式 Linux 开发的朋友大多都有过这样的经历板子跑起来了、业务功能正常了、开机速度也优化到几秒内了然后就觉得“这项目稳了”。但如果你做的是联网设备——不管是工业网关、边缘计算盒子、车载终端还是一个带 WiFi 的智能硬件——那你迟早会碰到安全这个躲不开的坎。我这次分享的是系列专栏里第 17 讲的内容主题就是嵌入式 Linux 的系统级安全加固。上一讲第 16 讲我们聊了内核启动与根文件系统的构建当时我给读者留了几道思考题这次我会先把题目做一个完整解析再顺着“最小化裁剪 → 权限硬化 → 日志审计 → 轻量防火墙”这条主线把整个安全加固的实操路径从头到尾捋一遍。为什么要把这几个动作放在一起讲因为嵌入式设备的安全问题从来不是某一个点上的疏漏而是一条链上的系统性问题。你的系统裁剪得再小如果权限一塌糊涂那攻击者照样能拿到 root日志配得再全如果没有防火墙兜底脆弱服务照样暴露在网络上。反过来如果只做防火墙而忽略裁剪和审计那也顶多算是“表面防御”。所以这一讲的核心思路是从系统的各个层面同时收口把攻击面压到最小把每个入口的权限收紧把所有关键行为记录下来再用防火墙把网络侧的暴露面挡住。这套思路适合谁不管你是刚入门嵌入式 Linux 的开发者还是在做产品量产前安全评估的工程师甚至是你只是想把自己手头那个“跑起来就行”的板子搞得稍微专业一点这篇文章的内容都应该能给你一些可以直接落地的思路和命令。2. 最小化裁剪攻击面小一分风险就少一分2.1 构建层面的“减法”思路很多嵌入式系统的镜像一开始是从开发板的出厂系统改过来的里面塞了一大堆你用不到的东西。串口调试工具、编译链、开发库、桌面环境甚至还有 Python、GCC这些在开发阶段是宝贝在量产设备上就是灾难。我见过一个实际案例某工业设备出厂时用的是全功能 Debian 根文件系统里面居然还带着 Perl 和一堆网络扫描相关的工具包。后来做安全评估时发现攻击者只需要利用一个 Web 服务的漏洞就能拿到 shell然后直接用系统自带的工具去扫描内网、抓密码整个过程轻松得让人后背发凉。所以最小化裁剪的第一条原则就是只保留业务运行所需的最小集合。怎么判断这个集合的边界我一般按这三步走先梳理业务进程的依赖链用ldd查看每个二进制依赖的共享库把busybox、glibc、内核模块这些基础件单独列出其余不需要的库一律不打包。再把所有网络监听端口列出来netstat -tulnp或者ss -tulnp凡是没在业务清单里的服务要么停掉、要么直接卸载。最后再检查文件系统里的“杂物”/usr/share/doc、/usr/src、.a静态库、开发头文件这些在量产镜像里应该统统消失。我习惯用Buildroot或者Yocto来构建最小系统而不是手动去删 Debian 的文件。因为手动删文件很容易删出依赖断裂而且每次换版本都要重新来一遍很容犯低级错误。用构建工具的话只需要在 menuconfig 里面把不需要的包取消掉重新编译一次就能得到一个小而干净的系统。2.2 内核裁剪与配置项取舍裁剪完了文件系统下一步就是内核。内核的裁剪不只是为了“让镜像更小”更是为了减少攻击面。你想想一个你根本用不到的 USB 驱动模块如果恰好有个漏洞被攻击者利用那你不是白白送了一个入口吗编译内核时有几个方向需要特别关注关闭不需要的网络协议和驱动。比如 IPC 设备一般用不上蓝牙、红外、NFC这些协议栈不仅占用内存还会增加内核态的攻击面。文件系统支持只保留需要的几种。常见的就是ext4、squashfs、overlayfs像什么cifs、nfs、vfat如果业务没必要就不要开着。内核模块的签名机制。如果你做的是安全要求较高的设备建议打开CONFIG_MODULE_SIG只加载有签名验证的模块防止攻击者往内核里插入恶意模块。打开常用的安全加固选项。比如CONFIG_STRICT_KERNEL_RWX、CONFIG_DEBUG_WX、CONFIG_HARDENED_USERCOPY这些选项在 Ubuntu 这种桌面发行版里默认是开的但很多嵌入式厂商的 BSP 里却常常被关掉理由往往是“怕影响性能”。实测下来这些选项对绝大多数业务场景的性能影响是可以忽略的但带来的安全收益却是实打实的。我个人在实际项目里有个习惯把内核配置文件和构建脚本一起提交到版本库这样每次发布固件时都能追溯用的是哪一套配置。如果想要更严谨一点可以用diffconfig脚本把 BSP 默认配置和你的安全裁剪配置做一次对比把差异项列出来写在发布说明里。2.3 只读挂载与文件系统完整性最小化裁剪的另一个重点是文件系统的“只读化”。把一个只读根文件系统打包成squashfs然后通过uboot加载到内存或者挂载到 overlayfs 的下层这样即使攻击者拿到了 shell也没办法往系统分区里写东西没法持久化后门。我做只读文件系统时的一般做法是用squashfs做只读根fs用overlayfs给需要写的目录比如/var、/tmp、/etc下需要动态修改的少量配置单独开一个可写层而且是 tmpfs重启即丢。这样系统逻辑上还是可以正常工作的但实际上“脏”数据根本没地方落盘。不过这里有个坑如果你的设备需要进行 OTA 升级只读文件系统会让升级策略变得复杂。业界常用的方案是把根文件系统做成 A/B 双分区升级时往另一个分区写然后切换启动项。这样的话即使升级写了一半断电设备也能从另一个分区启动不会变砖。这种方案对闪存容量的要求高一些但换来的是可靠性和安全性的双重保障。3. 权限硬化把系统的“门锁”全部换一遍3.1 用户、组与文件权限的基础配置很多嵌入式系统默认就跑着一个 root所有的业务进程也是 root 身份这个习惯在开发板上没什么问题但在产品上就是大忌。正确的做法是为每个业务服务创建独立的系统用户并赋予最小权限。举个例子你的设备上跑了一个 HTTP 服务那就创建一个www用户给它只分配 Web 根目录的读权限和日志目录的写权限跑了一个采集程序就创建sensor用户给它分配对应的设备节点和输出目录的权限。这样即使某个服务被攻破攻击者拿到的也只是那个用户的权限而不是整个系统的 root。文件权限这一块除了常规的chmod 644/755之外我还会关注几个特殊位suid/sgid位整个文件系统里应该尽可能少地出现 suid 文件。可以用find / -perm -4000查一遍凡是列表里出现的不明所以的 suid 程序都应该直接去掉 suid 位。全局可写文件/tmp、/var/tmp、/dev/shm这类目录本身需要全局可写但它们上面不应该挂任何重要的执行文件或配置。设备节点的权限/dev下的节点通常由devtmpfs/udev管理但如果你用的是静态设备节点的方式要特别注意那些如/dev/mem、/dev/kmem、/dev/sdX或其它能直接操作磁盘、物理内存的节点确保只有特定用户和组可以访问。3.2 能力机制Capabilities替代无脑 rootLinux 的 root 权限是“一刀切”的它把所有特权都赋给了一个用户。而capabilities机制把 root 的特权拆成了几十个细粒度的能力项比如CAP_NET_ADMIN网络配置、CAP_DAC_OVERRIDE绕过文件权限检查、CAP_SYS_TIME修改系统时钟等。你可以单独给某个二进制文件赋权让它在非 root 的情况下也能完成某类特权操作。实际操作中假设你的业务程序需要绑定 80 端口但这属于CAP_NET_BIND_SERVICE的范畴你完全可以用setcap cap_net_bind_serviceep /usr/bin/your_app给它授权而不是直接让它跑在 root 下。这招在嵌入式场景里特别实用因为很多设备的主业务进程是 C/C 写的功能复杂、代码量巨大指望它完全没漏洞不太现实。但如果你用 capabilities 把它的特权控制在极小范围那就算进程被攻破攻击者能做的事情也非常有限。不过要注意setcap依赖于文件的扩展属性你的文件系统必须是ext4、squashfs这类支持 xattr 的格式并且要确保启动脚本里没有setcap之后又被chmod或重新打包导致能力丢失的情况。3.3 seccomp 与系统调用白名单Capabilities 管的是“特权操作”的边界而 seccompsecure computing mode管的是“系统调用”的边界。对于嵌入式设备上的单一业务程序用 seccomp 做一个系统调用白名单能带来非常明显的安全收益。我做过一个实际的边缘网关项目业务程序是个 C 写的 MQTT 网关我只给它开放了大约 40 个系统调用包括read、write、poll、socket相关的、mmap、openat等等其余的系统调用全部返回错误。这样即使攻击者在程序里塞了恶意代码它连execve都调不了更别提起一个 shell 了。配置 seccomp 有几种方式直接用libseccomp库在代码里写过滤规则最灵活但需要重新编译。用systemd的SystemCallFilter指令大部分现代嵌入式系统用的都是 systemd加几行配置就行。用docker的--security-opt seccomp配置如果你的业务容器化的话。个人建议嵌入式主业务程序尽量用第一种方式把过滤规则写死在代码里不要依赖 systemd 的配置。因为有时候攻击者不只是打你的程序本身他可能会利用其他漏洞直接拿 root这时候 systemd 层面的过滤可能就被绕过了但代码内嵌的 seccomp 策略会是最后一道防线。3.4 服务管理与启动项的权限收敛嵌入式系统上跑的不止业务主程序一个进程很可能还有 SSH、远程管理 Agent、日志转发、看门狗等服务。这些服务如果运转在错误的高权限下那系统其他地方的加固都可能白费。用 systemd 管理服务的话我强烈建议在 service 文件里把以下几项都写清楚User和Group指定进程运行身份。NoNewPrivilegestrue禁止进程通过execve获得新权限。ProtectSystemstrict让整个文件系统对服务变成只读。PrivateDevicestrue隐藏大部分设备节点。ProtectKernelTunablestrue、ProtectControlGroupstrue防止内核参数和 cgroup 被篡改。RestrictSUIDSGIDtrue禁止创建 suid/sgid 文件。RestrictAddressFamiliesAF_UNIX AF_INET AF_INET6限制网络协议族。这些参数看上去可能觉得繁琐但其实都是一行配置的事加完之后你的服务就被关进了一个权限“沙箱”里。唯一要注意的是测试时可能会出现某些功能异常比如某个服务需要访问的设备节点被PrivateDevices遮住了这时候就需要有针对性地放行对应项而不是把整条安全策略去掉。4. 日志审计让每一次可疑行为都有迹可循4.1 日志体系怎么搭才完整很多嵌入式项目对日志的态度是“能打就行”出问题的时候再去翻串口输出。但在安全场景下日志不只是给开发调 bug 用的更重要的职责是记录“谁在什么时间做了什么操作”。所以日志体系至少要覆盖三层系统层内核日志、启动过程、文件系统挂载、服务启停。网络层连接来源、连接目标、端口访问记录。应用层业务操作的登录、登出、数据变更、远程控制指令。在嵌入式系统上日志服务比较轻量的组合是syslog/rsyslog加logrotate如果跑的是 systemd 环境journald也可以负责一部分。需要注意不要把日志全部放在本地磁盘因为攻击者拿掉 shell 后第一件事往往就是“清日志”。有条件的话通过 Syslog 协议把日志实时转发到远端的日志服务器这是安全审计里非常重要的一条。4.2 关键审计点登录、 sudo、内核与网络Linux 自带的 audit 子系统是做安全审计最强大的工具。它在内核态直接收集事件比应用层的日志更难以被普通用户绕过。嵌入式设备上虽然资源有限但也不建议完全关闭 audit而是要有选择地启用几类关键事件审计。我一般会先加上这几条规则记录所有与身份认证相关的事件-w /etc/passwd -p wa -k identity、-w /etc/shadow -p wa -k identity。记录用户/组管理的修改-w /etc/group -p wa -k identity、-w /etc/sudoers -p wa -k privilege。记录系统管理员执行的命令execve。记录网络配置的变更监控/etc/network、/etc/hosts*、/etc/resolv.conf等文件的写入。如果用的是 auditd要注意一个嵌入式环境下的常见坑auditd 在日志满的时候会进入“fail-stop”模式导致新的进程无法启动。这在开发机上没感觉但在内存小的设备上很容易触发。解决办法是同时配置好max_log_file、space_left、admin_space_left这几个阈值并让 disk_full_action 保持为 syslog 通知而不要设置为 halt。4.3 日志的轮转、加密与远端转发日志如果不做轮转小容量 Flash 很快就会被写满。嵌入式系统上我推荐按大小加时间的双重轮转策略单个日志文件不超过 2MB每天最多保留 7 份。通过logrotate的配置可以很容易实现/etc/logrotate.d/下面针对不同应用各写一份规则即可。日志内容的加密这块容易被大家忽略。攻击者如果想篡改日志最简单的办法就是直接改写文件而如果日志文件本身做了签名或者加密保存那即使被改了也会被发现。更实用一点的做法是最小化本地留存时间、优先远端转发。比如本地只保留最近 1 天的日志同时通过 TLS 加密的 Syslog 通道转发到日志服务器。这样既能看到最近的问题又能在事后追溯更长时间的数据。4.4 日志审计实战快速定位安全事件日志体系搭好之后真正到了排查安全事件的时候怎么用我举一个我实际碰到过的场景设备突然对外发起大量异常连接怀疑被植入恶意程序。当时我的排查路径是先看连接状态ss -tnp查哪个进程在对外建连。这一步很有用但很多攻击者会做进程隐藏所以不能只信这个。再看审计日志ausearch -k execve查看最近有哪些可疑的execve调用如果有一个你没见过的二进制路径出现了十有八九就是恶意样本。看网络连接审计有没有某个进程的connect事件发生在异常时间点。对照系统日志在恶意进程启动前系统日志里往往会留下异常的用户登录、session 打开、服务重启等记录。把这些信息拼起来基本就能还原出攻击者的完整路径。这也是为什么我一直强调日志体系必须在平时就建设好真出了事再补日志什么都晚了。5. 轻量防火墙嵌入式场景下的网络守门员5.1 嵌入式环境下该选 iptables、nftables 还是嵌入式专用的方案在嵌入式设备上做防火墙最常见的选项是 iptables 和 nftables。iptables 是老牌工具内核兼容性好资料多nftables 是新一代架构语法更清晰、规则更高效而且内存占用和规则匹配性能也比 iptables 好一些特别适合资源受限的设备。如果你的内核版本在 3.13 以上绝大多数现代嵌入式 BSP 都满足我个人更推荐用 nftables。它用一套简洁的“表table→ 链chain→ 规则rule”模型规则可以像写 JSON 那样分层组织调试起来也直观很多。另外如果你的设备资源非常紧张只有几百 KB 可用内存可以考虑一些面向嵌入式场景的极简防火墙方案比如直接在应用层做连接白名单。但这类方案通常只能做简单的 IP/端口过滤深度包检测、限速、状态跟踪都做不了。在预算允许的情况下用内核态的 nftables 是最稳的选择。5.2 nftables 规则设计实例白名单优先嵌入式设备的网络咱们就按实际的来讲默认策略应该是“白名单优先”也就是说没有明确允许的流量一律拒绝。下面是一个我在一个工业数据采集器上实际用过的规则集简版# 清空所有规则 nft flush ruleset # 创建地址族 inet 的表命名为 filter nft add table inet filter # 创建三条链分别是 input、forward、output nft add chain inet filter input { type filter hook input priority 0\; policy drop\; } nft add chain inet filter forward { type filter hook forward priority 0\; policy drop\; } nft add chain inet filter output { type filter hook output priority 0\; policy accept\; } # 允许回环接口 nft add rule inet filter input iif lo accept # 允许已经建立的连接及相关连接 nft add rule inet filter input ct state established,related accept # 允许 SSH 管理端口假设是 22但生产环境建议换掉 nft add rule inet filter input tcp dport 22 ip saddr 192.168.1.0/24 accept # 允许数据上报服务的流量假设是 MQTT 1883 nft add rule inet filter input tcp dport 1883 accept # 允许 ICMP ping 但限速防止顺手做探活扫描 nft add rule inet filter input icmp type echo-request limit rate 5/second accept # 其余输入全部拒绝链的 policy 已经是 drop这段配置里几个点值得解释priority 0表示默认优先级加反斜杠转义\;是因为分号在 nftables 里有特殊含义。回环接口lo必须放行否则本地进程之间的通信会挂掉。ct state established,related是状态防火墙的核心它允许出站流量对应的回应数据包回来但拒绝外部主动新建的连接。SSH 这句我特意加了ip saddr 192.168.1.0/24做来源限制意思是只有内网网段的机器才能 SSH 上来其他来源一律不给连。这在嵌入式设备上是很有用的控制手段。5.3 规则持久化与防火墙自启动嵌入式设备上配置防火墙有一个常见问题规则在重启后就丢了。iptables 时代可以用iptables-save /etc/iptables.rules配合启动脚本加载nftables 时代的做法也类似直接把规则集保存成文件nft list ruleset /etc/nftables.conf然后在 systemd 里启用 nftables 服务systemctl enable nftables systemctl start nftables需要注意的坑很多 BSP 里的 nftables 服务启动时机比较早可能比网络服务先启动这没问题。但如果你的规则里依赖ct state或者iifname这些需要网络接口存在的表达式建议在规则文件加载前先确认对应的网络接口已经初始化完成。我遇到过几次“规则看起来加载了但完全不生效”的情况排查半天发现是接口名还没出现规则匹配了个寂寞。5.4 调试防火墙规则拒绝把自己锁在外面配防火墙最尴尬的事就是把自己从 SSH 上踢下来。尤其你人在现场门都锁上了再想进去就得折腾串口或者干脆跑一趟机房非常痛苦。我的经验是所有防火墙规则在应用之前先写一个定时任务作为“逃生门”。比如# 新规则测试期先加一条定时清理规则的任务10分钟后清掉 echo nft flush ruleset | at now 10 minutes这样即使规则有问题导致你掉线10 分钟后规则会被清除你还能重新连上。确认规则没问题后再把at任务取消。另外测试时不要只开一个 SSH 窗口最好同时开着串口或者另外一个网络通道确保有回退路径。6. 第 16 讲课后思考题完整解析6.1 思考题回顾与考察点说明上一讲第 16 讲的内容是内核启动流程与根文件系统构建当时我留了 4 道思考题主要考察的是对启动流程的理解和对文件系统构建细节的掌握。本讲借着安全加固的话题先把这 4 道题过一遍因为里面有两个知识点和本讲的主题直接相关一个是“最小化裁剪怎么做”另一个是“启动阶段如何做安全校验”。6.2 题目一构建最小根文件系统时哪些组件是必须保留的完整答案需要从“启动链”和“运行依赖”两个角度分析。从启动链来看init第一个用户态进程、busybox或者你指定的 shell、mount工具、必要的动态链接器如ld-linux-armhf.so.3、以及挂载根文件系统所需的内核驱动模块这五类组件必不可少。从运行依赖来看你的主业务程序通过 ldd 关联的共享库必须保留除此之外的绝大部分组件都可以删掉。另外一个容易被忽略的点是/etc/inittabSysV init或者 systemd 的基础 unit 文件、/etc/fstab这些配置文件虽然不产生“程序代码”但它们是系统能以预期方式启动的关键。很多人在裁剪时只盯着大文件删把配置目录删漏了结果系统启动到一半就罢工。6.3 题目二为什么根文件系统不适合直接在 NFS 网络挂载下用于生产环境这题考察的是对“网络依赖”和“安全边界”的理解。NFS 挂载意味着根文件系统内容完全依赖网络可达性和 NFS 服务器的可信度如果网络路径被劫持整个系统都可能被“掉包”。同时 NFS 协议本身的认证机制薄弱在复杂网络环境下很容易出现未授权访问数据完整性也无法保证。量产设备上用得比较多的是本地 Flash 上的 squashfs/ext4 镜像配合只读挂载。这样不仅摆脱了对网络的依赖还能通过镜像的校验值比如写在 U-Boot 环境变量里来判断文件系统是否被篡改过。从安全角度来说“根文件系统必须来自本地可信存储”是一条底线这也是我之前给上一讲留下的核心观点之一。6.4 题目三内核启动参数里init/bin/sh是一把什么样的双刃剑往深了说这题是在考察对“Linux 启动链路权限边界”的理解。init/bin/sh意味着内核在挂载根文件系统后直接拉起一个 shell而不是执行/sbin/init。在调试阶段这能让你得到最原始的 shell 来排查问题实用价值确实高。但只要这个参数能通过外部输入被修改比如从串口、网络启动参数、甚至是 U-Boot 环境变量任何人都能拿到一个几乎无限制的 root shell之前做的那么多权限加固瞬间全部失效。所以生产环境必须做两件事一是锁定 U-Boot 环境变量和串口控制台访问二是让内核验证启动参数CONFIG_CMDLINE_EXTEND配合签名校验。如果条件允许还可以打开内核的CONFIG_LOCK_DOWN_KERNEL禁止运行时代码在 lockdown 状态下修改内核命令行参数。6.5 题目四如果让你重新设计上一个项目的文件系统布局你会做哪些改动这道题没有标准答案但好的回答通常能体现出“安全意识 可维护性”的平衡。比如我个人的回答会包含这些点根文件系统做成只读 squashfs上层叠加 tmpfs 的 overlayfs保证系统分区不可写。/var和/tmp放在内存文件系统里避免频繁写入 Flash 影响寿命同时让运行时数据不持久化。/etc里需要动态修改的配置文件单独挪到一个可写分区或者通过环境变量注入。把日志目录、OTA 下载缓存目录、数据库目录等相对独立、增长快的路径统一规划到一个数据分区方便做容量管理和清理策略。这些改动单独看似乎不复杂但组合起来会让整个系统的安全性和可运维性都上一个台阶。7. 整体加固方案落地一个典型的工业边缘网关实例7.1 需求背景与加固前的系统状态前面几章把各个模块拆开来讲了这一章我以一个“工业边缘网关”为例把整个加固流程串起来。假设这个网关的配置是Cortex-A7 双核 528MHz 处理器、256MB DDR、512MB eMMC跑的是厂商 BSP 自带的嵌入式 Linux内核版本 4.19业务上需要采集串口数据通过 MQTT 上报到云端同时支持远程 SSH 运维。加固之前这台设备的现状是root 登录、系统里带着一堆开发工具、防火墙规则为空、日志只存在本地且不做轮转。可以说任何一个接入局域网的攻击者都能很轻松地在五分钟内拿到这台设备的完全控制权。7.2 分阶段加固实施清单整个加固我分成了四个阶段每个阶段之间做个简单验证确认没有引入新的问题再进下一阶段第一阶段系统裁剪与只读化。用 Buildroot 重新构建根文件系统只保留 busybox、主业务程序、必要的 glibc 库、ssh serverdropbear用 OpenSSH 的话太胖、以及其他基础工具内核裁剪掉蓝牙、NFC、未用到的网卡驱动根文件系统打成 squashfs/var、/tmp 用 tmpfs/data 挂一个 ext4 分区。第二阶段权限硬化。创建app用户给主业务程序使用dropbear 禁止 root 登录对关键设备节点比如 /dev/ttyS* 设置 group 为 dialout 并给 app 用户赋读权限把 setcap 给需要用到的二进制加上主业务程序用 libseccomp 加白名单。第三阶段日志与审计。启用 systemd-journald 和 rsyslog、logrotateauditd 配置关键文件监控和 execve 审计写一个轻量脚本每隔 10 分钟检查一次关键目录下是否有新增的可执行文件通过 Syslog over TLS 把日志转发到内网日志服务器。第四阶段防火墙和网络侧收敛。用 nftables 配置白名单规则只允许 MQTT 到云端服务器的出方向连接允许内网网段的 SSH 入方向把没有用到的端口服务全部停掉修改 dropbear 的端口到非默认值同时禁用密码登录改用密钥认证。7.3 加固后的效果对比与实测数据这套流程走完我特意在测试环境里跑了几轮验证。用扫描器扫描加固前后的设备端口加固前开放端口包括 22、53、80、8080还有一个厂商自带的 5555 调试端口加固后只剩 22 端口对内网开放且来源被限制到了运维网段。紧接着做了一次弱口令爆破模拟加固之前的设备“坚持”了不到五分钟就被攻破加固之后爆破工具在 dropbear 上折腾了半天依然无法进入因为 root 登录被禁止、密码登录被关闭只有密钥才能认证。文件系统层面加壳之前攻击者拿到 shell 后可以任意写 /etc/rc.local 做持久化加固之后根文件系统是只读的连 /etc 都不可写所谓持久化后门根本无从谈起。这个效果的对比非常直观加固不是给系统增加多少新的功能而是让攻击者每一步都走不顺畅。7.4 常见坑和排查技巧汇总做这套加固时我也踩了不少坑写在这里给各位提个醒只读文件系统配好后某些软件启动时会在 /var 里写套接字文件如果你用的是 tmpfs 且大小配小了就会出现“service started but not actually working”的诡异问题。排查办法df -h看 /var 的使用率。seccomp 白名单加得太严业务程序后期加了一个功能结果触发未知系统调用导致进程被杀。排查办法先开启 SECCOMP_RET_LOG 模式跑一段时间测试确认所有系统调用都覆盖了再切换成 SECCOMP_RET_ERRNO。nftables 规则里如果写了interface name而不是iifname在接口名还不存在时规则匹配不到任何流量。排查办法nft list ruleset查看规则是否被正常加载再nft monitor trace跟踪具体包。auditd 日志全写满后可能导致系统其他进程无法启动特别是/var/log/audit空间不足的情况下。排查办法配置好space_left参数把disk_full_action设置为syslog而不是halt。dropbear 禁用了密码登录后如果同时禁了 root但你的运维人员习惯用密码登录那你就等着被现场电话轰炸吧。建议先跟团队确认好密钥认证的流程再真正关掉密码登录。8. 最后再分享一个小技巧加固状态的“体检脚本”整套加固做完之后我习惯在生产设备上跑一段自检脚本用来确认安全配置没有被无意中改掉。这个脚本并不复杂主要检查几件事是否有用户可以直接无密码登录检查 /etc/shadow 里空密码字段。是否存在异常 suid 文件扫描整个根文件系统。根文件系统是否以只读方式挂载mount | grep / 看 ro 标志。nftables 规则是否还在nft list ruleset非空。关键配置文件的校验值用sha256sum和预存值对比。监听端口清单ss -tulnp如果出现了不在白名单里的端口就触发告警。把这个脚本放到/etc/cron.d/security_check里每 10 分钟执行一次发现问题就往日志服务器推一条告警。实际上很多安全事故都是“渐进式”的——攻击者先拿下一个不重要的点然后慢慢渗透如果你有一个周期性的体检脚本就有机会在他深入之前发现问题。根据我个人的经验嵌入式 Linux 的安全加固从来不是一个“做完就结束”的动作而是一个需要持续跟进的状态。等到你的设备真的被攻击了再去补墙那种感觉非常被动。在项目初期就把裁剪、权限、审计、防火墙这四件事纳入开发计划每一轮迭代都过一遍检查清单虽然前期会多花一些时间但后面省下的运维和信任成本值太多了。
返回列表