一提到“实时Linux工业PLC”大多数人第一反应都是延迟、抖动、调度精度这些性能指标。但我在实际项目中越来越发现真正让项目后期头疼的往往不是性能而是安全——不是那种“防外部黑客”的宏大叙事而是非常具体的谁有权限动这个运行几十万设备的生产控制系统一个普通运维账号误操作能不能把实时进程搞崩一个临时调试进来的脚本会不会把CPU核心占满导致PLC控制周期抖动我印象最深的一次某现场的产线设备在凌晨周期性出现几十毫秒级别的控制超时查了一个多星期最后定位到一个“看门人”式的内部问题一位同事抱着笔记本电脑接入调试网顺手跑了一个数据同步脚本脚本的CPU占用直接把实时核给抢了。那一刻我意识到一件事实时Linux PLC的安全防护不只是防外部的恶意攻击更要防内部的误操作、越权操作和资源抢占。权限管控不到位再好的实时性能都是纸面数据。这篇文章就围绕这个主题讲讲我在实时Linux PLC项目里沉淀下来的安全防护与权限管控思路兼顾安全加固同时保证不伤实时性。1. 实时Linux PLC的安全边界先搞清楚要防谁、为什么难防做安全防护之前必须先把边界画清楚。实时Linux PLC的部署环境、运行负载、维护模式和普通IT服务器差别很大直接套用通用安全模板要么防护无效要么误伤实时链路。这块我吃过亏先把它讲透。1.1 实时性与安全性之间的拧巴关系实时Linux系统无论是PREEMPT_RT内核还是双内核方案的核心目标是让关键任务在确定时间内被调度执行。为了这个确定性很多常规的“安全机制”反而成了干扰源。最典型的例子是内存锁定实时任务为了保证不发生缺页中断会调用mlockall把内存页锁在物理内存里。这是必要手段但对系统来说这意味着大量内存不可换出普通进程的压力会变大。反过来如果为了安全把内存限额卡得太紧实时任务的锁页请求失败任务直接起不来或者运行异常。另一个例子是CPU隔离。为了减少调度抖动项目里常见的做法是用isolcpus或cpuset把若干个CPU核心隔离出来专门跑实时任务。这个机制本身是“性能手段”但它自动变成了一道资源边界普通进程默认跑不到那些核上。可要是你没有配好irqaffinity和内核线程的迁移中断和内核线程照样会落到隔离核上实时任务还是会被打断。所以我说安全性和实时性是拧巴的防护措施要么少做要么做错中间很难拿捏。从安全视角理解这句话实时系统里CPU时间、内存锁页、中断响应能力都是稀缺且不可抢占的资源。权限管控的本质不是单纯限制“谁能登录”而是限制“谁能占用这些资源”。这是和普通服务器权限管理最大的差异点。1.2 攻击面不是只对外更要对内大多数现场环境里实时Linux PLC所在的工控网段是封闭的和办公网之间有隔离。于是很多项目团队会产生一种错觉边界隔离做好了内部就不需要精细管控了。这是最危险的想法。我梳理过这类系统的实际攻击面大致可以分成三层远程网络面通过工业协议比如Modbus/TCP、OPC UA暴露出来的控制端口以及为远程维护开放的SSH、VNC、Web配置界面。这一层外部黑客可能触及但更多时候是内部人员从办公网跳板机进来操作。本地物理面USB口、维护网口、调试串口。现场工程师、设备厂商调试人员、甚至保洁人员都可能物理接触设备。一个插入的U盘、一根调试串口线比任何网络攻击都直接。内部操作面合法账号的越权操作、误操作、恶意操作。这是实际发生频率最高的“攻击面”但它往往被忽视因为操作者本身有权限只是“不该这么用”。我见过不少项目的默认状态是所有工程师用一个公共root账号或一个共享admin账号登录PLC主机密码贴在设备外壳上。一旦有人误删了日志目录、改了实时任务调度参数你连是谁改的都无法追溯。所谓安全在这种模式下等于零。1.3 通用服务器安全方案为什么不能直接搬有人可能会说安全防护不就是关端口、开防火墙、配SELinux、上堡垒机吗这些思路本身没错但直接套在实时Linux PLC上会出问题。先说SELinux。SELinux的强制访问控制确实很强但它的策略配置复杂而且对进程调度、中断绑定、设备节点访问的管控粒度很细——配置不当很容易导致实时任务无法绑定CPU、无法访问特定设备节点。我在一个模拟项目上开过SELinux enforcing模式结果实时任务在启动时被拒绝设置调度策略直接退出。不是SELinux不好而是它的学习曲线和策略开发周期跟工业现场的交付节奏不匹配。再看堡垒机和跳板机。这东西在IT机房好使但在工厂车间设备可能部署在高温、潮湿、空间狭小的控制柜里网络结构还可能带着环网冗余你很难要求所有操作都经过一个中心化的跳板机。等你想通过跳板机远程处理问题现场可能已经停产了。所以我的观点是实时Linux PLC的安全防护应该以“在设备本地形成纵深防御”为核心远程审计辅助配合。重心放在最贴近实时任务的那一环上。这样既能保住实时性又能在出问题时快速定位、快速恢复。2. 最小权限落地从账号体系到能力集裁剪防护思路确定之后第一步要做的是权限的精细化管控。这里的核心原则是让每个角色只拥有完成自己工作所需的最小权限尤其要把“能影响实时任务”的能力收敛到极少数受信的账号上。普通服务器那样一堆root满天飞的做法在实时控制系统里必须彻底抛弃。2.1 角色拆分运行用户、维护用户、审计用户我习惯把PLC主机上的账号分成三类每类职责清晰权限互不交叉运行用户专门跑PLC运行时Runtime和实时任务的低权限用户该用户对应用目录有读和执行权限对配置目录可能只有读权限。它的核心诉求是能启动实时任务、锁定内存、设置实时调度但绝不应该有修改系统配置、安装软件的能力。维护用户现场运维工程师或调试工程师使用的账号具备查看日志、查看实时状态、临时修改部分配置的权限。需要实施变更操作时通过sudo提权完成并且每一次提权操作都会被记录。审计用户只具备读取审计日志和系统日志的权限账号本身不做其他事情。这个账号是给合规审计用的我目前看到真正按这个思路建的团队很少但一旦出事它的价值是巨大的。落地时可以用系统自带的账户体系实现不复杂。例如在Debian/Ubuntu系里# 创建运行用户不设登录 shell禁止交互登录 useradd -r -s /usr/sbin/nologin plcservice # 创建维护用户组允许作为运维账号加入 groupadd -r rt-maintainer useradd -m -G rt-maintainer maint-ops # 把实时任务的启动做成 systemd 服务或专用脚本归属 plcservice这里有一个关键细节不要让PLC运行时直接以root启动。很多人图省事把软PLC运行时跑在root下觉得这样省去权限适配的麻烦。实际隐患极大——一旦运行时进程被攻破或者被人注入了恶意代码攻击者就直接拿到了整个系统的控制权。把它降权到专用账号即使运行时本身出问题攻击面也被限制在最小范围内。2.2 capabilities裁剪而不是滥用root实时任务有一个特殊需求它需要设置实时调度策略比如SCHED_FIFO或SCHED_RR同时对指定内存范围执行mlock。这些操作在传统Unix模型里都需要root权限。如果把整个进程放到root下运行等于为了两个小权限放弃了整个安全边界。Linux的capabilities机制正好解决这个问题。它可以把root的权限拆分成几十个独立的能力项按需赋予进程。实时PLC运行时通常需要的核心能力就两个CAP_SYS_NICE允许设置进程优先级和调度策略也就是能让实时任务用上SCHED_FIFO。CAP_IPC_LOCK允许锁定内存也就是执行mlock/mlockall时不会被拒绝。如果运行时还需要绑定到特定CPU调用sched_setaffinity通常不需要额外能力但需要确认SELinux或AppArmor不拦着。给运行时二进制文件直接挂能力的方式是sudo setcap cap_sys_nice,cap_ipc_lockep /opt/plc/bin/plc-runtime # 验证能力是否生效 getcap /opt/plc/bin/plc-runtime这样做的好处是即使有人窃取了运行用户的Shell假设它被允许登录这个Shell进程也不具备实时调度和锁内存的能力捣乱的成本和破坏面都会小很多。注意setcap设置后二进制文件一旦被修改文件的capabilities信息会被内核自动清空这也天然提供了一层完整性保护。2.3 文件系统、服务配置与默认拒绝策略账号权限和capabilities只解决“谁可以做什么”的问题文件和目录本身的权限也得跟上。我通常会按下面这个思路做文件系统权限规划应用目录如/opt/plc归属root:plcservice目录权限750普通用户不可读写。配置目录只给运行用户读权限写权限只给维护组通过sudo操作。日志目录给运行用户写权限但历史日志的轮转和归档由独立进程负责防止应用自己写坏。脚本目录里的调试、测试脚本一律不放在生产路径下更不要以root执行。系统服务方面建议裁剪掉PLC主机上不需要的服务。什么打印机、蓝牙、Avahi、Cups该停则停。SSH如果必须开至少做三件事禁止root直接登录、只允许密钥认证、监听在管理网段的独立地址上而不是所有接口。# /etc/ssh/sshd_config 关键配置 PermitRootLogin no PasswordAuthentication no AllowUsers maint-ops audit-view一个容易被忽略的地方是sudo规则。我见过很多sudoers配置写的都是maint-ops ALL(ALL) ALL等于给维护账号开了无限root权限跟给root账号区别不大。建议把sudo权限细化到具体命令比如只允许systemctl restart plc-runtime、systemctl status plc-runtime、tail/less日志等。sudoers里配多了出问题的时候审计日志才有意义。3. 实时资源隔离防护措施不能反过来破坏实时性权限管控做完之后下一步是给实时任务建立一道“资源隔离墙”。这一层既要防止非实时负载抢占CPU、内存和IO带宽又要保证隔离机制本身不引入新的不确定性。这里面的每一步选择背后都和实时性有直接关系。3.1 CPU隔离与中断绑定的组合我在实时Linux系统上跑PLC时最推荐的做法是把CPU划分为“实时核”和“非实时核”。实时核专门跑PLC运行时和IO任务非实时核跑管理服务、Shell交互、日志收集。这样即使维护人员跑一个吃CPU的脚本它也只会占用非实时核影响不到实时循环。具体的实现方式有两类一类是内核启动参数isolcpus另一类是cpuset控制组。isolcpus简单直接设置后普通用户态进程默认不会再被调度到隔离核上除非显式设置affinity适合项目初期快速验证。cpuset更灵活可以在系统运行过程中动态调整但配置复杂度高一截。推荐的内核参数组合示例isolcpus2,3 nohz_full2,3 rcu_nocbs2,3isolcpus把2、3号核隔离出来。nohz_full让这两个核尽量不接收周期性时钟中断减少定时器抖动。rcu_nocbs把RCU回调任务移走降低隔离核上的内核后台负载。但只做CPU隔离还不够。如果不处理中断网络中断、磁盘中断照样会落在隔离核上。现场常见的坑是网卡中断绑定在CPU2上实时任务也跑在CPU2上结果每来几个网络包实时任务就被中断打断一次。解决方法是手动设置中断亲和性把网卡、磁盘的中断都赶去非实时核配合irqbalance的禁用策略。# 查看网卡中断号 cat /proc/interrupts | grep eth0 # 将指定中断绑定到 CPU0-1假设为 67 号中断 echo 3 /proc/irq/67/smp_affinity推荐把CPU0和CPU1留给系统管理、中断处理CPU2和CPU3给实时任务并且把实时任务的sched_setaffinity显式指定到CPU2、3上。这样配合起来实时核才真正“干净”。3.2 内存锁页既有实时需求又有安全隐患实时任务的代码路径上不应该出现缺页异常因为缺页导致的内核处理时间不可控。所以要给实时进程锁页。但锁页这个操作本身就是一个需要严格权限管控的敏感操作。如果进程被赋予了CAP_IPC_LOCK能力它可以把大量物理内存锁住。一旦失控会导致系统内存枯竭影响所有进程。我的做法是双限度约束对运行用户设置Locks锁页内存额度上限通过/etc/security/limits.conf配置避免单个进程锁太多内存。对锁页总量做监控阈值告警。写一段简单的脚本定时检查/proc/meminfo里的Mlocked字段超过预设值就告警。# /etc/security/limits.conf plcservice hard memlock 131072 # 单位 KB即 128MB plcservice soft memlock 131072为什么对实时任务还要卡上限因为实时控制系统的内存需求通常是明确可算的不是越多越好。卡住上限还能防止程序内存泄漏时系统直接瘫痪。给足正常运行所需空间留出余量做告警阈值这样既保实时又保系统稳定。3.3 cgroup/进程守护把负载关进笼子如果现场历史包袱重没法用isolcpus做硬隔离退化版本的替代方案是用cgroup做资源限制。比如把管理进程和后台任务放进一个限制CPU使用率的cgroup即使它们出问题也不能无限抢占CPU。这不算完美的实时隔离方案但对很多既有系统是成本最低的加固手段。例如限制某个组最大使用1个CPU核的一半50%可以用cgroup v2的cpu.max配置# 创建 cgroup 并限制 cpu 使用量 mkdir -p /sys/fs/cgroup/ctrl-plane/limit-group echo 50000 100000 /sys/fs/cgroup/ctrl-plane/limit-group/cpu.max此外我还习惯给PLC运行时配置systemd守护方式而不是裸脚本nohup跑。systemd的Restarton-failure可以自动拉起异常退出的实时进程TimeoutStartSec和TimeoutStopSec可以避免维护时操作卡死。但要注意不要把StopWatchdogSec设得太短否则内核panic级别的故障还没处理完systemd就强制杀进程反而破坏故障现场。从权限角度看systemd unit的权限控制也能做得比较细。比如通过Userplcservice指定运行身份配合AmbientCapabilitiesCAP_SYS_NICE CAP_IPC_LOCK替代全局setcap使配置随服务管理更清晰可控。# /etc/systemd/system/plc-runtime.service [Service] Userplcservice Groupplcservice AmbientCapabilitiesCAP_SYS_NICE CAP_IPC_LOCK CapabilityBoundingSetCAP_SYS_NICE CAP_IPC_LOCK Restarton-failure ExecStart/opt/plc/bin/plc-runtime这些配置组合起来效果很好进程身份低权限、能力最小化、资源受限、自动恢复四个维度把实时任务保护得很严实。4. 审计与异常检测安全兜底靠的是每一条记录权限管控做得再好也保不齐有人跳过流程、有人账号被冒用、有脚本行为异常。这时候日志和审计是唯一能还原现场的东西。但日志这个事在工业现场最容易被人敷衍过去——默认syslog开着就算完事没人细究哪些关键操作被记录了日志会不会被篡改以及有没有人真的去看。4.1 auditd监控哪些点最划算Linux自带的auditd是实现操作审计的利器。它不需要侵入应用只在内核层记录指定对象的事件。对实时PLC系统我建议优先监控几个点系统关键文件/etc/shadow、/etc/passwd、/etc/sudoers、/etc/ssh/sshd_config的读、写和属性变更。PLC运行时相关的二进制和配置/opt/plc/bin/plc-runtime、/opt/plc/config下的写操作和属性变更。用户sudo提权动作所有提权命令都要记录。内核模块加载、系统服务的启停。示例规则# 监控关键认证文件 auditctl -w /etc/shadow -p wa -k auth-files auditctl -w /etc/sudoers -p wa -k sudo-config # 监控 PLC 目录变更 auditctl -w /opt/plc/bin/plc-runtime -p wa -k plc-bin auditctl -w /opt/plc/config -p wa -k plc-conf # 记录所有 sudo 命令执行 auditctl -a always,exit -F archb64 -S execve -F auid1000 -F auid!4294967295 -k user-command真正排查问题的时候一条审计记录比什么都管用。它能看到是哪个用户、从哪个终端、在什么时间改了文件精确到进程号。有一次项目上PLC运行时配置被误改导致控制逻辑异常靠audit日志定位到具体账号和操作时间十分钟就还原了事情经过避免了全组人拍脑袋猜原因。4.2 日志不给篡改机会只读挂载与远程转发默认情况下/var/log/audit/下的日志root是可以改的。如果作案者本身就是管理员log可能被抹掉。工业现场很多时候所谓的“内部调查”第一步就是确保日志没被改过。因此日志防护要两条腿走路。第一条把日志目录做成独立的只读挂载。这不是普遍做法但对关键工程站是可行的把/var/log挂到一个独立分区系统正常运行时mount成只读通过日志守护进程用特定方式写日志比如借助rsyslog的omfile模块配合文件可写属性。这个方案对多数工程团队来说配置成本偏高我一般只在最核心的PLC站做。第二条远程日志转发这是最实际也最推荐的做法。把PLC主机上的audit日志和系统日志实时转发到专门的日志收集服务器日志收集服务器放在管理网段不和生产控制环直接交互。这样即使本地日志被清除远端仍然保留历史记录。用rsyslog转发很简单# /etc/rsyslog.d/remote-logger.conf *.* 192.168.60.10:514生产环境中我还会给远程日志通道加TSL加密避免日志内容在传输过程中被截获。如果不想引入复杂的日志中心至少也要做到每天把日志备份出一份加密归档放在不同设备上。4.3 文件完整性校验怎么适配实时工控场景auditd记录的是“谁在什么时候动了什么”文件完整性校验解决的是“关键文件到底有没有被动过”。经典工具是AIDE或Tripwire通过为关键文件生成校验基线再定期比对来发现未授权的变更。但直接把这些工具周期性扫描搬到实时PLC上要小心性能干扰。全盘扫描时CPU和IO压力都不小可能影响实时任务。我的适配方案是只配置PLC运行时、配置文件和系统认证文件这几个关键目录不要扫整个文件系统。校验时间放在维护窗口或现场不生产的低峰期用nice和ionice设置低优先级。扫描结果输出到独立日志文件由审计员确认不和业务日志混在一起。下面是AIDE配置的简化示例# /etc/aide/aide.conf /etc/shadow piugn /etc/passwd piugn /opt/plc/bin/plc-runtime pinugsmd5sha256 /opt/plc/config piugmd5初始化基线之后每次维护操作完都可以手动跑一次校验很快就能发现“某个不该动的文件变了”。时间一长这套基线的价值比任何防病毒软件都靠谱因为它针对的是这个系统自己的关键资产而不是通用特征库。5. 一次“可疑负载”排查的完整链路从现象到加固前面讲了很多防护原则但读者可能还是想知道这些东西串起来到底怎么工作我用一个我之前参与的疑难点排查过程来做复盘。这是一个模拟场景但里面的排查链路完全可以复现到实际项目里。5.1 现象控制周期抖动且只在凌晨出现当时有一套基于实时Linux的PLC系统控制器运行稳定了几个月。忽然某天现场反馈设备主轴在凌晨两三点会出现周期性抖动持续几分钟后恢复正常白天基本不出问题。控制周期有的周期延迟从微秒级涨到了几十毫秒虽然不至于立刻停机但已经影响产品质量。按照常规思路优先排查网络。因为抖动发生在凌晨现场怀疑可能是交接班时有数据批量同步、有设备在跑批量任务。检查了交换机的端口统计、CPU占用、中断频率都没发现明显异常。网络层面的怀疑被排除目光转向PLC主机本身。5.2 逐层排查登录记录、audit日志、实时任务状态接下来我按安全排查的思路做了这几步第一步看系统当前登录会话和最近登录记录。用last和lastlog发现凌晨时间点有一个维护账号通过SSH登录过系统登录后还在终端上停留了一段时间。这个账号属于一个外部设备厂家的调试工程师平时来现场做设备参数校准。第二步查audit日志看这个会话里到底执行过什么。audit日志显示该账号通过sudo执行了一个数据采集脚本脚本持续运行了大概三分钟之后就退出了。这台PLC主机本身没有部署数据采集应用脚本很可能是调试工程师自己带上来的配套工具。第三步查实时任务在那一刻的状态。从内核的调度统计里看到实时控制线程在抖动时间窗内出现了明显的调度延迟而延迟的根源是有大量进程竞争CPU。控制线程所在的CPU核上CPU占用率在脚本运行期间被拉满而脚本进程的CPU亲和性没有限制内核把部分负载调度到了实时核上。整个链路到这里已经很清楚了外部账号通过合法通道进入系统执行了未授权的负载型脚本脚本消耗了实时核的CPU资源间接破坏了控制周期。这既不是病毒也不是外部入侵而是一次典型的“合法账号的越权行为”。5.3 定位元凶与加固措施验证定位到具体账号和具体脚本后项目组做了三件事收回该外部厂商账号的sudo权限只保留查看日志和特定调试命令的执行权限所有变更操作必须走变更申请流程由内部人员执行。给PLC主机加上CPU隔离配置把实时任务绑到专用核上同时设置中断亲和性。这样即使是内部维护账号再跑类似脚本也只能占用非实时核的资源不影响控制周期。在audit规则中增加对该账号执行命令的强制审计并把日志实时转发到远程日志服务器。加固后我特意在那个账号上重新模拟跑了一次同样的脚本控制任务延迟完全不受影响。非实时核CPU占用再高实时核依旧稳如磐石。后续这个系统又运行了半年多抖动再没出现过。这次排查让我坚定了两个判断一是实时Linux PLC的问题很多时候要从权限视角找原因二是安全加固不是给运维添麻烦反而是帮运维把问题隔离在可控范围内让大家能睡个安稳觉。6. 长期维护让安全配置在设备生命周期内持续有效安全防护不止是项目交付那一刻做完就结束了。工业PLC设备动辄运行五到八年期间固件要升级、配置要调优、人员会流动安全配置很容易在一次次“临时操作”中被悄悄绕过。我个人的经验是把这几个环节做成固定流程才能让安全真正持续有效。6.1 变更窗口与基线管理我建议在每个PLC工程项目里建立一份“安全基线档案”内容包括账号清单、sudo规则、能力集配置、文件完整性基线、审计规则、CPU与中断绑定配置。每次做变更升级内核、改配置、换PLC程序之后留一个专门的验证步骤对照基线检查权限配置有没有被意外改动。跑一次文件完整性校验把结果归档。开一个短周期的实时性测试确认加固措施没有引入新的延迟。这套流程听着繁琐实际消耗的时间不多。但它的价值在于某次变更导致的问题可以在发生前被拦下来而不是等产线上出问题再来排查。6.2 密钥轮换与固件更新密钥和证书的管理经常被忽视。SSH密钥一旦发放出去如果没有轮换机制离职人员和外部厂商手里的密钥会一直有效。我现在的做法是密钥有效期默认90天到180天到期强制更换外部人员的密钥到期后不自动续签必须重新走审批流程。固件更新方面实时Linux PLC对内核版本和补丁是敏感的不建议盲目追新。但也不能因为怕影响实时性就永远不更新。折中方案是在一个离线测试环境里先用相同硬件和相同负载跑通更新验证实时性指标达标后再排计划在生产设备上一次性更新更新后立即跑完整性校验和实时性测试。6.3 写在最后的一点体会做实时Linux PLC安全防护这几年我最深的感受是安全方案做得再花哨最后都要落到“有人真正执行”上。权限管控的目标不是戒备每一个人而是让正常的操作顺畅无阻让异常的操作留下痕迹、寸步难行。只要账号体系、资源隔离、审计检测这三大块能扎扎实实地落到每一台PLC主机上实时性不会被安全拖后腿安全也不会被实时性牺牲。这套思路无论项目大小都能用得上建议所有正在做或准备做实时Linux工控项目的团队尽早把它纳入系统设计而不是等出了问题再补课。
阅读完成 · 觉得有帮助?