上周一个客户把已经量产一年的网关产品退了回来,说设备被人拿去当跳板,远程日志里全是扫描流量。我拿回来一查:root 默认密码没改,telnetd 还开着,防火墙规则是空的,日志审计也没有接到后端。这不是个例,嵌入式 Linux 设备在安全层面普遍处于裸奔状态。咱们专栏这一讲就专门解决这个问题,把最小化裁剪、权限硬化、日志审计、轻量防火墙这四件事拆开讲透,最后把第16篇的课后思考题完整解析放出来。
1. 先从攻击面说起:嵌入式设备为什么总被盯上
1.1 嵌入式设备的“裸奔”现状
嵌入式 Linux 设备的处境比服务器尴尬得多。服务器有专职运维盯着,补丁跟着 CVE 走,默认口令通常被改掉,网络边界上有硬件防火墙和入侵检测设备。嵌入式产品呢?量产后部署在客户现场,可能几年不更新,root 口令是出厂写死的,telnet 或者弱 SSH 还开着,系统里灌了一堆用不上的应用、内核模块和调试工具。更麻烦的是,你没有远程运维通道去感知它是否已经被入侵。
我在很多项目里见过这样的现象:开发阶段为了调试方便,把 telnetd、tftpd、gdb 全部打进镜像,发布的时候忘了关;又或者内核编译时图省事,把能用到的不能用到的一股脑编进去,包括一大堆文件系统驱动、网络协议和调试接口。攻击者不需要多高深的技术,扫描到开放端口,试一下默认口令,进去以后发现是 root,整个设备就归他了。
1.2 威胁模型:你的设备可能面对谁
做安全加固之前,先得想清楚你的设备可能被谁盯上,这决定你愿意花多少成本去加固。
- 线上自动化扫描蠕虫:它们不在乎你是谁,只扫常见端口和默认口令,命中就植入僵尸网络。这类攻击量最大,但技术含量低。
- 定向攻击者:可能是看到你这个产品有利可图,或者你的设备被当作进入内网的跳板。他们会分析固件、找后门、利用内核漏洞,目标是长期潜伏。
- 物理接触者:拿到设备的人可能接串口、拆 Flash、引导一个自己的内核,把文件系统拎出来分析。这类攻击最难防,但通常威胁模型里要识别“设备是否在可控环境中运行”。
对多数物联网和工业产品来说,排在第一位的是自动化扫描和弱口令爆破,所以先把基础动作做扎实,收益最高。
1.3 安全加固的四个目标维度
这个系列的安全加固思路归纳起来是四件事:减少暴露面、提高攻击成本、保留追溯能力、保证可维护性。
- 减少暴露面:砍掉用不上的软件、端口、内核模块,让攻击者没有入口。
- 提高攻击成本:默认不信任任何进程,即使通过应用漏洞拿到了 shell,也没法轻易提权。
- 保留追溯能力:把登录、配置变更、异常行为记录下来,出事以后能还原现场。
- 保证可维护性:加固不能做得设备无法升级、无法调试。安全不是一次性工作,必须能持续迭代。
这四个目标贯彻到下面的每个章节里。你会发现很多动作其实不复杂,复杂的是要不要在量产前坚持做。
2. 最小化裁剪:从内核、文件系统到服务,把不需要的全部拿掉
2.1 为什么“代码越少越安全”不是玄学
安全圈常说的“attack surface”不是空话。一个内核模块和一段网络服务代码,理论上都有漏洞的可能;只要它在系统里跑着,被攻击的概率就大于 0。反过来,如果这个功能根本不存在,攻击者连利用的入口都没有。
最小化裁剪的目的不是节省几百 KB 存储,而是把潜在漏洞从“可能被利用”变成“不存在”。比如你根本不需要 Bluetooth 协议栈,那么 BlueZ 相关的 CVE 就跟你无关;不需要 U 盘自动挂载,那么热插拔相关的逻辑就不会成为入口。裁剪得越干净,需要维护和审计的代码就越少。
2.2 内核侧裁剪:关模块、关调试、关不需要的协议
内核是裁剪的第一站。用 Buildroot 或者直接从 kernel.org 拉源码编译,都推荐在make menuconfig里逐项过一遍,而不是用发行版内核配置。
我列一个嵌入式设备做安全加固时经常关掉的选项清单:
| 类别 | 典型选项 | 说明 |
|---|---|---|
| 调试设施 | CONFIG_KALLSYMS、CONFIG_DEBUG_FS、CONFIG_MAGIC_SYSRQ | 降低信息泄露和异常触发面 |
| 文件系统 | CONFIG_FUSE_FS、CONFIG_CIFS、CONFIG_NFS_FS | 不在产品上产生的读写需求直接关掉 |
| 网络协议 | CONFIG_BT、CONFIG_IP_DCCP、CONFIG_NET_SCTP | 蓝牙、DCCP、SCTP 等协议按需保留 |
| 设备驱动 | CONFIG_USB_GADGET、CONFIG_RAW_DRIVER、CONFIG_IEEE1394 | 没有对应硬件就不编入驱动 |
| 内存设备 | CONFIG_DEVKMEM、CONFIG_DEVMEM | 如果用不到直接关掉 |
| 模块加载 | CONFIG_MODULES | 如果确定不需要动态加载模块,可以关掉或只读加载 |
需要说明的是,CONFIG_MODULES关不关要看产品需求。如果固件必须支持后续 OTA 升级内核模块,那就保留,但建议加上内核模块签名校验,避免被植入恶意模块。另一个常被忽视的点是/proc/kcore、/dev/mem、/dev/kmem这类接口,它们在内核配置里能通过CONFIG_PROC_KCORE、CONFIG_DEVMEM控制,生产环境一律关闭。
串口调试接口也有讲究。开发板把console=ttyS0留着方便调试,但产品在外运行时,如果串口暴露在外部接口,任何人都能接上去看到内核日志甚至进入单用户模式。建议量产后把串口登录关掉,只保留内核日志输出,或者干脆通过 GPIO 拨码开关控制是否启用调试口。
2.3 rootfs 裁剪:BusyBox、工具链和出厂配置
内核之外,rootfs 是裁剪的重点。很多镜像里塞了 gcc、make、gdb、perl、python、tcpdump,这些工具在开发机上很有用,但在产品上全是风险。
在 Buildroot 里,BusyBox 的配置可以用BR2_PACKAGE_BUSYBOX_CONFIG指向一个裁剪过的 busybox.config,把不需要的 applet 拿掉。基本原则是:每个 applet 都是一个小入口,没需求就不要放进去。常见可移除项包括:
telnetd、tftpd、ftpd、httpd:除非产品真的对外提供这些服务。gcc、gdb、make、strace、tcpdump:调试工具只留在开发镜像里。login、su:如果生产环境没有本地用户登录需求,反而少了一大类爆破入口。wget:如果不需要远程拉取文件,把 wget 也拿掉,少一个攻击面。
工具链也同理。交叉编译工具链和 sysroot 属于构建主机,不需要运行在产品里。/usr/include下的头文件、静态库、未用的动态库,统统删掉。
服务也是一个道理。如果产品里只有一个业务进程,连 crond 都可以不要。定时任务改成主程序里内置定时器,或者用 systemd timer、runit 这类轻量方式管理。不要按照通用服务器习惯去安装一堆守护进程。
2.4 裁剪之后的验证清单
裁剪完不能直接打包发布,至少要做一轮“攻击面检查”。我自己在项目里常用几条命令,允许的话你可以直接抄:
# 查看当前监听端口,确认只开放了预期服务 ss -lntup # 查看开机自启动服务,有没有不该出现的 ps -ef # 查看内核已加载模块,逐项确认必要性 cat /proc/modules # 查看所有 SUID/SGID 文件,数量越少越好 find / -xdev -type f \( -perm -4000 -o -perm -2000 \) -ls # 查看系统内所有可执行二进制,确认没有编译器或调试器 find / -xdev -type f -executable裁剪之后镜像体积变小是锦上添花,真正的收益是暴露面大幅收缩。你做完这些动作,再去看ss -lntup的输出,应该只有业务服务和一个受控的远程管理通道。如果还有莫名其妙的东西在监听,说明裁剪还不到位。
3. 权限硬化:不是只改个 chmod,而是让系统默认就不信任任何进程
3.1 用户模型:让应用以最低权限运行
很多嵌入式设备的业务应用习惯用 root 跑,理由是“方便访问 GPIO、I2C、网络接口”。这个习惯不改,前面裁剪做得再好也白搭。攻击者拿到一个远程命令执行漏洞,如果进程是 root,直接就是最高权限接管设备。
正确的用户模型是:系统里保留一个用于管理维护的普通用户,业务进程单独建一个专用用户,比如app,并且只给它访问必要资源的权限。设备节点、GPIO、I2C 设备可以用 udev 规则或用户组来授权,而不是让进程跑在 root 下。
举个例子,如果应用只访问/dev/fb0、/dev/input/eventX和/dev/ttymxc0,那就写一个 udev 规则把这些设备节点分给app组,然后让应用以app身份启动。这样即使应用被攻破,攻击者面对的是一个没有特权 shell 的低权限用户。
SSH 和远程管理也要遵守这条原则。Dropbear 或者 OpenSSH 的PermitRootLogin必须设为no,管理员用普通用户登录后再通过受控方式切换。不要为了图省事,让 root 直接通过 SSH 登录。
3.2 文件系统权限:SUID 清理与挂载选项
Linux 权限硬化的一个重要工作是清理 SUID/SGID 文件。这些文件执行时会临时获得属主的权限,一旦有漏洞,攻击者很容易利用。前面验证清单里的find / -xdev -type f \( -perm -4000 -o -perm -2000 \) -ls就是干这个的。
发现系统里确实存在 SUID 文件后,先确认是不是需要它。常见的ping需要 SUID 才能创建原始 socket,如果你用不到 ping,或者可以给ping设置 capabilities,就直接去掉 SUID 位:
chmod u-s /bin/ping挂载选项是另一个被忽视的方向。/tmp、/var、/dev/shm 这些可写目录,建议在/etc/fstab里加上nosuid、nodev、noexec:
tmpfs /tmp tmpfs defaults,noexec,nosuid,nodev,size=16M,mode=1777 0 0 tmpfs /dev/shm tmpfs defaults,noexec,nosuid,nodev,size=1M,mode=1777 0 0根文件系统如果支持只读挂载,尽量这样做。产品运行只需要读写/data或/var之类的独立分区,根文件系统只读后,攻击者想往/usr/bin写后门、改/etc/passwd、替换系统库就变得非常困难。这也是为什么很多设备把/etc做成只读,配置文件通过一个小分区覆盖。
不要忘了把动态库搜索路径收紧。默认情况下进程可能通过环境变量LD_PRELOAD劫持库调用,这是常见提权手段。如果系统支持,设置/etc/ld.so.preload内容为空,并且在启动脚本里清理可疑环境变量。更严格的做法是用只读的库目录和内核配置来限制。
3.3 内核运维参数:改完立刻有效的安全开关
有一批内核运维参数,不需要重新编译内核,改完立即生效,对提升安全性效果明显。推荐做成/etc/sysctl.d/99-security.conf:
# 隐藏内核指针,防止泄露内核地址 kernel.kptr_restrict=1 # 限制非特权用户读取内核日志 kernel.dmesg_restrict=1 # 限制 ptrace 任意进程调试 kernel.yama.ptrace_scope=1 # 禁止内核接受 ICMP 重定向 net.ipv4.conf.all.accept_redirects=0 net.ipv4.conf.all.send_redirects=0 # 开启反向路径过滤,防 IP 欺骗 net.ipv4.conf.all.rp_filter=1 # 开启 SYN Cookies,缓解 SYN Flood net.ipv4.tcp_syncookies=1 # 忽略 ICMP 广播 ping net.ipv4.icmp_echo_ignore_broadcasts=1 # IPv6 同样关掉重定向 net.ipv6.conf.all.accept_redirects=0其中kernel.kptr_restrict=1和kernel.dmesg_restrict=1是我最看重的两项。它们阻止普通用户通过/proc/kallsyms和dmesg拿到内核内存布局,对攻击者利用内核漏洞造成很大阻碍。
如果你的内核已经开启了CONFIG_SECURITY_DMESG_RESTRICT,那么dmesg_restrict这个参数直接生效;如果内核配置里没有开,你需要回到内核裁剪阶段确认。这也是安全和系统构建耦合的一个典型例子。
3.4 Linux Capabilities与强制访问控制的取舍
有时候某个程序确实需要某个特权,比如要绑定 80 端口,但不想让它拥有整个 root。这种情况不要用 SUID,可以用 capabilities:
# 给 /usr/bin/webserver 绑低端口的能力 setcap 'cap_net_bind_service=+ep' /usr/bin/webserver # 查看已设置的能力 getcap /usr/bin/webserver # 如果不需要了,移除 setcap -r /usr/bin/webserverCapabilities 相当于把 root 权限拆成原子能力,用多少给多少,非常合适嵌入式这种“一个设备只干一件事”的场景。
至于 SELinux、AppArmor 这些强制访问控制,在嵌入式上要权衡。SELinux 的 policy 编写成本高,内存和闪存开销也不小;AppArmor 相对轻一些,Yocto 的 meta-security 层有现成支持。如果产品对隔离要求很高,但资源又受限,我会选择用 mount namespace + chroot 或者一个轻量容器方案,把业务进程跟系统隔开,而不是硬上 SELinux。
这里我没有给出一刀切的答案,因为不同产品形态差异太大。但思路是一致的:默认不信任,最小授权,能隔离就隔离。
4. 日志审计:安全事件发生之后,你得有“回放”的证据链
4.1 日志审计要回答的三个问题
没有日志,安全加固做得再好,出了事也是两眼一抹黑。日志审计要回答三个问题:谁来过、做了什么、怎么进来的。
- 谁来过:记录 SSH 登录成功/失败、串口登录、su 切换用户的事件。
- 做了什么:记录配置变更、固件升级、防火墙规则修改、关键文件访问。
- 怎么进来的:通过内核日志和网络日志,识别扫描行为、暴力破解、异常连接。
很多嵌入式工程师觉得日志是“软需求”,排不上优先级。但真实事故处理里,设备被入侵后如果没有日志,你连攻击途径都说不清,只能整体报废。这个教训我吃过不止一次。
4.2 嵌入式环境下的日志方案选型
通用服务器上装 rsyslog + auditd 是常规操作,但嵌入式设备资源紧张,抉择要更务实。
- 如果只是需要系统日志、业务日志和登录日志,BusyBox syslogd 足够。它轻量,支持本地写日志,也支持远程转发。
- 如果想把不同来源日志结构化处理,可以上 syslog-ng 或者 rsyslog,但要注意内存和 CPU 占用。
- auditd 在嵌入式上通常不推荐,它的资源开销和规则复杂度对小型设备不友好。可以退而求其次,对关键行为在应用层自己记录,比如开机启动脚本里把关键操作输出到 syslog。
我最常用的方案是 BusyBox syslogd + 应用日志直接写/data/log,再通过 logrotate 做轮转。启动脚本里告诉 syslogd 写到哪里:
syslogd -O /data/log/messages -l 7-l 7表示记录所有优先级日志。如果担心日志量太大,可以调低,比如只记录 err 以上的级别。
4.3 持久化、轮转与防篡改
日志最大的敌人是丢失和篡改。很多设备把/var/log放在 tmpfs 上,断电就没了。生产环境一定要把日志写到持久化分区,比如/data/log。
logrotate 配置我一般这样写:
/data/log/messages { rotate 7 daily compress delaycompress maxsize 8M missingok notifempty }如果不想装 logrotate,也可以写一个最小的 shell 脚本来做轮转,但 logrotate 在多数嵌入式镜像里已经有,直接用更省事。
防篡改方面,权限足够低就行。日志目录不要给业务进程写权限,只有log用户或 root 能写。有条件的情况下只读挂载日志盘,或者对日志文件做chattr +a(只允许追加,禁止删除和覆盖)。对于攻击者已经拿到 root 的场景,任何本地防篡改都没用,所以核心日志一定要远程同步一份。
4.4 审计的“最后一公里”:同步时钟与远程收集
日志信息不能回溯时间,价值就大打折扣。嵌入式设备没有可靠 BIOS 电池,上电后时间可能停留在 1970-01-01。所以必须在启动后做时间同步,推荐用一个轻量 NTP 客户端:
# 伪代码示例,具体工具按镜像选择 ntpclient -s -h pool.ntp.org如果产品处于隔离网段无法访问外网 NTP,至少要在网关或上位机提供一个本地时间源。时间同步后,日志里的事件顺序才有意义。
远程日志收集我建议默认开启。简单方案是 BusyBox syslogd 直接转发到日志服务器:
syslogd -R 192.168.10.5:514 -L如果担心明文裸奔,可以在内网 VLAN 里跑,或者对日志通道做认证与加密。远程日志的价值在于:设备被彻底格式化、Flash 被擦除后,你依然有证据链。这也是整个日志审计计划里最有保险意义的一环。
5. 轻量防火墙:几十KB的规则,挡住大部分自动化攻击
5.1 为什么嵌入式设备也要配防火墙
嵌入式设备不是不能装防火墙,而是很多工程师觉得“我没有公网 IP,不需要”。等到设备被扫描器打穿就晚了。无论设备在什么网络环境,只要它能联网,就应该有一套“默认拒绝”的本地流量规则。
防火墙的价值不是解决高级攻击,而是挡住那些每天都在发生的自动化扫描和端口爆破。一个默认 drop 的 input 链,能直接过滤掉大量来自外部网络的无效探测。规则集不需要复杂,几十行就够了,对系统的开销可以忽略不计。
5.2 用 nftables 落地最小规则集
新内核我推荐直接使用 nftables,它是 iptables 的现代替代品,语法清爽,执行效率也更好。一个嵌入式终端设备的最小规则集大概是这样:
#!/usr/sbin/nft -f flush ruleset table inet filter { chain input { type filter hook input priority filter; policy drop; # 回环接口放行 iif "lo" accept # 已建立连接放行 ct state established,related accept # 内网管理网段 SSH 放行 ip saddr 192.168.1.0/24 tcp dport 22 accept # 公网 SSH 限制频率,防暴力破解 tcp dport 22 limit rate 4/minute accept # 业务端口按需放行 tcp dport 8080 accept # 所有其他新连接丢弃 drop } chain forward { type filter hook forward priority filter; policy drop; ct state established,related accept } chain output { type filter hook output priority filter; policy accept; } }默认策略是 drop,意味着没有明确放行的流量一律丢弃。输出方向一般默认允许,因为设备需要主动发心跳、上报数据;如果你的设备是纯受控终端,可以把 output 也收紧,但通常不建议,否则调试和升级会被自己搞死。
5.3 防暴力破解与异常流量限速
针对 SSH 暴力破解,上面用limit rate 4/minute已经可以挡住人工爆破,但对分布式底池还需要更细的规则。如果只允许特定网段 SSH,那就直接ip saddr 192.168.1.0/24 accept,其余全部 drop,这是最强的“防爆破”。
如果必须开放公网 SSH,还可以结合set做动态封禁,比如同一个源 IP 连接失败超过 3 次,加入 deny 集合:
table inet filter { set ssh_bruteforce { type ipv4_addr flags timeout } chain input { ... ip saddr @ssh_bruteforce drop tcp dport 22 ct state new add @ssh_bruteforce { ip saddr timeout 10m } } }这只是思路示意,具体语法根据内核和 nftables 版本可能会有差异,但方向是对的:用状态表把可疑来源限制住,而不是死扛所有流量。
5.4 双栈与规则持久化:最容易翻车的两个点
我在实际项目里发现两个特别容易踩的坑。
第一个是 IPv6 被忽略。很多工程师以为设备只有一个 IPv4 地址,结果系统默认启用了 IPv6 SLAAC,攻击者通过 IPv6 地址绕过 ip4 防火墙规则直接访问服务。要么在系统层禁用 IPv6,要么用table inet filter同时覆盖 v4/v6。千万不要只配 IPv4。
第二个是规则没有持久化。调试的时候在命令行敲了nft add ...,当时工作正常,重启之后规则全部丢失。正确做法是把规则放到/etc/nftables.conf,并且确保开机脚本用nft -f /etc/nftables.conf加载。如果 rootfs 只读,也需要在只读前把规则固定进去。
防火墙规则尽量少依赖外部的动态配置。生产设备不要给普通管理员开放“改防火墙规则”的便捷能力,因为改错一条规则,安全防线就开了一个口子。
6. 第16篇课后思考题完整解析:从镜像构建到应用安全
6.1 题目一:最小化裁剪到什么程度才算安全且可用
上一讲我们讲到从零开始构建一个完整的嵌入式 Linux 系统镜像,很多同学问:裁剪到什么程度才算“安全且可用”?
先回答“可用”的标准:这个镜像必须能完整跑通产品的核心功能。所以在裁剪之前先列功能测试矩阵,把网络、显示、存储、外设、升级流程逐项记录下来。裁剪之后必须把这个矩阵重新跑一遍,任何一项不过都不能发布。
再回答“安全”的标准:用第2.4节的检查命令做一轮攻击面检查。开放端口数量是否等于业务需要的最小集合?进程列表里是否只有必要的守护进程?SUID 文件数量是否为 0 或者接近 0?内核模块是否都能讲清楚用途?如果这几个问题的答案都严格,裁剪就是到位的。
6.2 题目二:AWTK 在嵌入式 Linux 上如何以最小权限运行
上一讲有同学提到用 AWTK 做嵌入式 Linux 的图形界面,这是一个很典型的应用层组件。AWTK 程序通常需要访问 framebuffer、输入设备和共享内存,但不等于要给 root。
建议的部署方式是:系统里创建app用户,把/dev/fb0、/dev/input/eventX通过 udev 规则分配给app组,然后用普通用户启动 AWTK 程序。如果程序需要绑定网络端口,用 capabilities 授予指定能力,而不是直接 root。
这道题的价值在于它体现了一个常见安全隐患:图形界面向来是攻击入口,如果界面应用以 root 身份运行,一个渲染漏洞就可能让攻击者拿到设备控制权。AWTK 本身不背这个锅,问题在于你的部署策略。
6.3 题目三:U 盘测速方案与安全加固有什么关系
这道题看似和加固无关,其实考的是综合理解。U 盘测速常用的方案是用dd写大文件:
# 写入测试 dd if=/dev/zero of=/mnt/usb/test.bin bs=1M count=256 conv=fsync # 读取测试 dd if=/mnt/usb/test.bin of=/dev/null bs=1M count=256也可以用hdparm -t测读取缓存性能。测完之后要删除测试文件,避免在只读 rootfs 和可写分区之间形成混淆。
安全角度:U 盘是攻击者可以物理接触的介质,必须关注自动挂载和恶意文件执行的风险。挂载 U 盘时建议使用nosuid,nodev,noexec选项,避免插入特制 U 盘后自动执行脚本。裁剪内核时还要注意保留需要的文件系统模块,比如 FAT/exFAT,否则测速没问题,真机使用却无法识别 U 盘。
6.4 题目四:调试口关闭后,如何快速定位问题
这是嵌入式开发中一个很现实的痛点。产品上线后不能像开发板那样随时接串口看日志,所以调试工具和调试口关闭后,必须有替代方案。
我的建议是多层日志体系:内核日志通过log_buf_len扩大环形缓冲区;应用日志写持久化分区;关键操作记录到远程日志服务器。同时保留一个受控的本地调试开关,比如通过硬件拨码、GPIO 或管理口控制是否开放串口登录。这样现场维护时能临时介入,平时又不暴露。
安全不是把调试功能一删了之,而是把调试能力藏在一个受控的通道后面。否则设备出问题只能返厂,实际上是付出更大的维护成本。
6.5 题目五:学习路线图里,安全加固应该放在哪个阶段
这道题没有标准答案,我给一个基于自身经验的排序。先学系统构建,至少要独立从零构建一个可以启动的嵌入式 Linux 系统镜像;再学应用开发,让自己真正写过跑在板子上的业务程序;安全加固应该紧接着系统构建之后,因为你已经理解了内核、rootfs、启动流程,再去理解权限、防火墙、日志会容易得多。
面试时如果被问到“安全加固”,不要只背术语。讲清楚你做过哪些事:内核怎么裁剪、sysctl 配了什么、防火墙默认策略是什么、日志怎么远程收集。面试官想听到的是你把安全动作落到了项目里,而不是只有概念。
7. 说点实操层面的体会:安全加固的边界和长期维护
7.1 加固不是一次性的上线动作
安全加固的真正难点不在写规则,而在持续维护。设备发布三个月后,业务加了一个新功能,需要开放一个新的 TCP 端口;团队为了赶进度,直接在生产镜像上打开防火墙,却没有同步更新/etc/nftables.conf。下一次重启,规则恢复原样,端口变得不可用,有人索性停掉防火墙服务。这种事情我见过太多次。
所以安全基线要和产品版本绑定,每次发版都要重新做攻击面检查,把加固清单当成发布流程的一部分。不要等出事之后再补。
7.2 我的常用检查命令一条龙
我现在养成了一个习惯,每次刷完一台新设备或者准备发布前,都会执行一遍这条“巡检命令链”:
ss -lntup ps -ef cat /proc/modules find / -xdev -type f \( -perm -4000 -o -perm -2000 \) -ls sysctl -a | egrep '(kptr_restrict|dmesg_restrict|accept_redirects|rp_filter|tcp_syncookies)' nft list ruleset这套命令没有魔法,但能让我在五分钟内了解这台设备处于什么安全状态。如果发现任何一项不符合预期,就直接打回修复。
另外还会检查默认口令和写死的凭据。很多设备在代码里硬编码了密钥、Token、默认 root 口令,发布时没有换成每台设备唯一的值。这是最高危的问题,因为自动扫描器第一个试的就是默认口令。
7.3 别把安全做成不可维护
见过一些团队把安全加固做到极端:rootfs 完全只读、所有配置固化、调试口全关、日志不落本地。结果产品在线上一出问题,连排查入口都没有,只能整机返厂。
我的建议是保留一个“逃生通道”,但不是默认开启。比如给现场维护人员一个独立的维护口,通过证书认证,默认关闭,设备故障时用专用工具打开。安全要在风险和可维护性之间找平衡,而不是把设备做成一个完全封闭的盒子。
说实话,嵌入式 Linux 的安全没有太多高深技巧,靠的是把最小化裁剪、权限硬化、日志审计、轻量防火墙这些基础动作做到位,并且在整个产品生命周期里坚持做。我在这个行业待得越久越觉得,真正让设备免于劫难的,不是某个安全产品,不是你写了几百行规则,而是你愿不愿意在这些琐碎的事情上较真。