工业边缘的网络边界正在变得模糊:设备远程运维、云边协同、移动终端接入、第三方系统集成,都会让原本“内网即可信”的假设失效。零信任架构的核心不是部署一个安全产品,而是把访问决策从“网络位置”改为“身份、设备、上下文和策略”共同决定。
本文按工程落地顺序梳理工业边缘零信任的核心原则、身份层、网络层、授权层、监测响应层,以及老设备与协议约束下的渐进式实施方法。
一、为什么工业边缘需要零信任
传统工业网络常见做法是划分为办公网、生产网、DMZ,再依靠防火墙隔离。这个模型在封闭产线中有效,但下面几类场景会逐渐削弱边界:
| 场景 | 边界假设被破坏的点 |
|---|---|
| 远程运维 | 外部人员需要访问网关、PLC 或 Historian |
| 云边协同 | 边缘网关持续访问云端 API、模型服务或配置中心 |
| 第三方集成 | 供应商系统、运维平台、数据分析平台互相调用 |
| 设备维修与更换 | 现场工程师笔记本、诊断工具、临时设备进入产线 |
| 内部横向移动 | 一台失陷设备尝试扫描或访问其他控制单元 |
零信任要解决的不是“把所有东西锁死”,而是让每一次访问都有明确的主体、设备、资源、动作和风险依据,并把权限限制在必要范围内。
更准确地说,零信任是一组安全架构模式:
- 默认不因请求来自内网而信任;
- 对人、服务、设备和网关分别建立身份;
- 授权基于最小权限和业务上下文;
- 网络按业务单元分段,限制横向移动;
- 认证、授权和会话状态持续评估;
- 认证和授权结果可审计、可回溯。
二、核心原则
1. 显式验证
每次访问至少要回答四个问题:
| 问题 | 常见证据 |
|---|---|
| 谁在访问 | 用户账号、服务账号、 workload 身份、设备证书 |
| 用什么设备访问 | 设备指纹、证书、资产编号、补丁状态、是否受管 |
| 访问什么资源 | API、MQTT Topic、PLC 数据点、运维入口、数据库表 |
| 上下文是否正常 | 时间、来源网络、地理位置、请求频率、历史行为 |
OAuth 2.0 解决的是授权委托,OIDC 才是在其上做身份认证的协议。工业系统里还要区分人的身份、设备身份和服务身份,不能把一个长期共享 API Key 当成完整身份体系。
2. 最小权限
权限应该按角色、资源和动作建模。例如:
- 只读巡检账号不能下发控制指令;
- 数据采集服务只能发布指定 MQTT Topic;
- 运维工程师只能访问授权站点和指定网关;
- 第三方分析平台只能读取脱敏后的聚合数据;
- 管理员操作需要审批、留痕和二次认证。
最小权限不是把权限一次性设计到最细,而是先从粗粒度资源隔离开始,逐步细化到动作、站点、设备组和数据范围。
3. 假设已被入侵
按“攻击者已经进入某个网段”来设计防御:
- 生产控制区与管理区、云边通道、运维通道分开;
- 网关之间避免无差别互通;
- 关键操作要有审批、双人复核或操作窗口;
- 横向访问默认拒绝,业务需要再显式放行;
- 安全事件发生时能隔离单台设备或单个网关。
4. 优先保护传输与敏感数据
“加密一切”需要结合设备能力落地。对工业边缘更务实的做法是:
| 数据类型 | 建议 |
|---|---|
| 云边通信 | 默认 TLS 1.2+,重要链路使用 mTLS |
| 服务间通信 | 使用 mTLS 或服务网格策略 |
| 本地敏感配置 | 使用密钥管理或系统凭据存储,避免明文入库 |
| 控制指令与审计日志 | 保护完整性,保留不可抵赖的操作记录 |
| 高频时序数据 | 按敏感度选择加密、签名或分段隔离 |
加密解决窃听和篡改风险,但不能替代认证与授权。只加 TLS 而不做设备身份和权限控制,仍然不是零信任。
三、参考架构与决策链路
一个简化后的零信任决策链路如下:
请求方(人 / 设备 / 服务)
|
| 凭据: OIDC Token、mTLS 证书、设备指纹
v
策略执行点(PEP: 网关 / API Proxy / Broker / Service Mesh)
|
| 输入: 身份、设备状态、资源、动作、上下文
v
策略决策点(PDP: IdP + 设备清单 + OPA + 风险服务)
|
+--> 允许 / 拒绝 / 要求二次认证 / 降低权限
|
v
审计日志与监测系统
几个工程要点:
- IdP 负责人的身份与 MFA;
- 设备清单负责设备资产、归属、健康状态和证书状态;
- OPA 或授权服务负责统一策略判断;
- 网关、代理、Broker、Service Mesh 是实际执行点;
- 日志系统必须记录决策依据,而不是只记录成功或失败。
不要试图让每个设备都直接对接所有安全系统。边缘侧更适合由网关或代理收敛协议差异,把 Modbus、OPC UA、MQTT、HTTP 等访问转换为统一的身份、授权和审计模型。
四、身份层:OIDC 与 MFA
1. OIDC 登录示例
下面示例使用 FastAPI、Authlib 和 Keycloak。实际部署时要通过环境变量或密钥管理系统读取 client_secret 和 session_secret,并确保应用时钟与 IdP 同步。
import os
from authlib.integrations.starlette_client import OAuth
from fastapi import FastAPI, Request
from fastapi.responses import RedirectResponse
from starlette.middleware.sessions import SessionMiddleware
app = FastAPI()
app.add_middleware(
SessionMiddleware,
secret_key=os.environ["SESSION_SECRET"],
https_only=True,
)
oauth = OAuth()
oauth.register(
name="keycloak",
client_id=os.environ["OIDC_CLIENT_ID"],
client_secret=os.environ["OIDC_CLIENT_SECRET"],
server_metadata_url=(
"https://keycloak.local/realms/edge/.well-known/"
"openid-configuration"
),
client_kwargs={"scope": "openid profile email"},
)
@app.get("/login")
async def login(request: Request):
redirect_uri = request.url_for("oidc_callback")
return await oauth.keycloak.authorize_redirect(request, redirect_uri)
@app.get("/auth/callback", name="oidc_callback")
async def oidc_callback(request: Request):
token = await oauth.keycloak.authorize_access_token(request)
userinfo = token.get("userinfo")
if userinfo is None:
return RedirectResponse("/login", status_code=303)
request.session["user"] = {
"sub": userinfo["sub"],
"name": userinfo.get("name", ""),
"email": userinfo.get("email", ""),
}
return RedirectResponse("/", status_code=303)
工程补充:
- 登录态 Cookie 设置
Secure、HttpOnly和SameSite; - 对运维入口、控制指令、批量导出启用 MFA;
- Logout 时同时清理本地会话和 IdP 会话;
- 服务账号使用独立凭据或 workload identity,不共享用户 Token;
- Token 过期时间要与风险等级匹配,不能为了方便设置过长有效期。
2. TOTP MFA 示例
import os
import pyotp
def enable_mfa(user_id: str) -> dict:
secret = pyotp.random_base32()
save_mfa_secret(user_id, secret)
provisioning_uri = pyotp.totp.TOTP(secret).provisioning_uri(
name=user_id,
issuer_name="edge.local",
)
return {"secret": secret, "provisioning_uri": provisioning_uri}
def verify_mfa(user_id: str, code: str) -> bool:
secret = get_mfa_secret(user_id)
if not secret:
return False
totp = pyotp.TOTP(secret)
return totp.verify(code, valid_window=1)
valid_window=1 允许前后各一个 30 秒时间窗,用来容忍合理时钟偏差,不是无限放宽校验。MFA 还要配套:
- 限制验证失败次数,防止暴力尝试;
- 提供恢复码,但恢复码同样要加密存储;
- 管理员重置 MFA 时要留审计记录;
- 现场无手机信号时,可使用硬件令牌或离线 TOTP。
五、设备与网络层:设备信任与 mTLS
1. 设备信任
工业边缘的“设备信任”通常来自组合证据:
| 证据 | 说明 |
|---|---|
| 设备证书 | 由内部 CA、供应商 CA 或平台签发,支持轮换与吊销 |
| 资产归属 | 设备属于哪个站点、产线、系统所有者 |
| 固件与补丁状态 | 版本、签名校验、已知漏洞状态 |
| 运行状态 | 是否在线、重启异常、配置漂移、异常外联 |
| 硬件能力 | 是否支持安全启动、可信存储、远程证明 |
设备信任不是一次认证后永久有效。证书临近过期、固件长期未更新、出现异常外联时,都应降低信任等级或要求人工确认。
2. Istio 开启命名空间级 mTLS
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: edge
spec:
mtls:
mode: STRICT
这份策略要求 edge 命名空间内的服务间通信使用 mTLS。它解决的是通信双方身份加密与认证,还需要配合 AuthorizationPolicy 控制谁能访问哪个服务:
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: allow-gateway-to-device-api
namespace: edge
spec:
selector:
matchLabels:
app: device-api
action: ALLOW
rules:
- from:
- source:
principals:
- cluster.local/ns/edge/sa/edge-gateway
to:
- operation:
methods: ["GET", "POST"]
paths: ["/api/v1/devices/*"]
mTLS 证书管理要点:
- 优先使用短周期证书和自动轮换;
- 建立证书吊销和设备注销流程;
- 保留证书指纹与设备资产的映射;
- 证书只证明密钥归属,不等于设备无害;
- 网关离线时也要能执行本地策略,恢复连接后再同步状态。
六、授权层:OPA 策略
OPA 适合把授权规则从业务代码中拆出来,统一维护“谁能做什么”。下面策略表示:只有通过认证、完成 MFA、来自受管设备、角色匹配,且当前上下文不异常时才允许访问。
package edge.authz
import rego.v1
default allow := false
allow if {
input.user.authenticated == true
input.user.mfa_verified == true
input.device.trusted == true
input.action.method in allowed_methods[input.action.type]
input.user.role in allowed_roles[input.resource.type]
not is_anomalous_request
}
is_anomalous_request if {
input.context.ip_changed == true
input.context.time_since_login > 3600
}
allowed_methods["read"] := ["GET"]
allowed_methods["write"] := ["POST", "PUT", "PATCH"]
allowed_methods["command"] := ["POST"]
allowed_roles["device"] := ["admin", "operator"]
allowed_roles["settings"] := ["admin"]
allowed_roles["command"] := ["admin", "operator"]
对应的输入示例:
{
"user": {
"authenticated": true,
"mfa_verified": true,
"role": "operator"
},
"device": {
"trusted": true,
"id": "gateway-021"
},
"resource": {
"type": "device"
},
"action": {
"type": "read",
"method": "GET"
},
"context": {
"ip_changed": false,
"time_since_login": 600
}
}
授权策略要进入版本管理,并配套单元测试:
package edge.authz_test
import rego.v1
operator_read_input := {
"user": {"authenticated": true, "mfa_verified": true, "role": "operator"},
"device": {"trusted": true},
"resource": {"type": "device"},
"action": {"type": "read", "method": "GET"},
"context": {"ip_changed": false, "time_since_login": 600}
}
no_mfa_input := {
"user": {"authenticated": true, "mfa_verified": false, "role": "admin"},
"device": {"trusted": true},
"resource": {"type": "device"},
"action": {"type": "read", "method": "GET"},
"context": {"ip_changed": false, "time_since_login": 600}
}
test_allow_operator_read_device if {
data.edge.authz.allow with input as operator_read_input
}
test_deny_without_mfa if {
not data.edge.authz.allow with input as no_mfa_input
}
策略变更前至少验证三类用例:允许、拒绝、需要提权。不要只测正向路径。
七、监测与响应
零信任的“持续验证”不能只靠登录时的一次认证,还要结合会话期信号。下面的风险评分是示例,重点在于输出可解释的决策原因,而不是把分数本身当成安全能力。
from dataclasses import dataclass
@dataclass
class RequestContext:
user_id: str
device_id: str
is_new_device: bool
geo_anomaly: bool
unusual_hour: bool
unusual_volume: bool
ip_changed: bool
class ZeroTrustMonitor:
def build_reasons(self, ctx: RequestContext) -> tuple[int, list[str]]:
rules = [
("new device", 30, ctx.is_new_device),
("geo anomaly", 40, ctx.geo_anomaly),
("unusual hour", 20, ctx.unusual_hour),
("unusual volume", 30, ctx.unusual_volume),
("ip changed", 25, ctx.ip_changed),
]
reasons = [name for name, _, hit in rules if hit]
score = sum(weight for _, weight, hit in rules if hit)
return min(score, 100), reasons
async def evaluate_request(self, ctx: RequestContext) -> bool:
score, reasons = self.build_reasons(ctx)
if score >= 70:
await self.deny_and_alert(ctx, score, reasons)
return False
if score >= 40:
await self.require_step_up(ctx, score, reasons)
else:
await self.log_decision(ctx, score, reasons, "allow")
return True
监测信号要贴合工业现场:
- 固定站点突然出现异常来源;
- 运维会话访问范围超出授权站点;
- 设备在非维护窗口尝试控制指令;
- 数据采集频率、Topic 和目标地址突变;
- 证书即将过期或校验失败;
- 网关反复离线、重启或配置回滚;
- 同一账号在多个地点并发登录。
响应动作可以分级:
| 风险等级 | 动作 |
|---|---|
| 低 | 记录并继续观察 |
| 中 | 要求 MFA、缩短会话、限制只读 |
| 高 | 拒绝访问、告警值班人员 |
| 严重 | 隔离设备、吊销凭据、阻断外联 |
八、微分段与协议约束
工业协议的现实约束不能忽略:
| 设备类型 | 约束 | 建议做法 |
|---|---|---|
| 老旧 PLC / RTU | 无 mTLS,弱认证或无认证 | 放入独立分段,由网关代理访问 |
| Modbus RTU / TCP | 功能码权限粒度有限 | 在网关侧限制 Slave、寄存器范围和功能码 |
| OPC UA | 支持用户认证与证书 | 启用安全模式、证书信任列表和节点级授权 |
| MQTT 设备 | 常见共享账号 | 使用每设备凭据、Topic ACL 和客户端证书 |
| 云边通道 | 出口固定 | 出站 allowlist、mTLS、请求签名 |
对不支持现代身份体系的设备,用工业网关作为策略执行点是常见落点:
运维人员 --OIDC/MFA--> 边缘网关 --Modbus/OPC UA--> 现场设备
|
+--> 设备清单 + 授权策略 + 审计日志
这样不需要立刻改造所有 PLC,也能先把“谁能访问、能读什么、能写什么、何时访问”管起来。
九、渐进式落地路线
| 阶段 | 目标 | 关键动作 |
|---|---|---|
| 1. 资产盘点 | 先知道保护什么 | 建立设备、协议、账号、端口和依赖清单 |
| 2. 身份收敛 | 消除共享账号 | 管理员接入 IdP,服务使用独立凭据 |
| 3. 代理执行 | 不直连高危资源 | 通过网关、代理或 Broker 统一入口 |
| 4. 策略细化 | 默认拒绝 | 按资源、角色、动作和网络分段授权 |
| 5. 监测闭环 | 发现异常行为 | 记录决策、风险信号和操作审计 |
| 6. 应急演练 | 验证可恢复性 | 演练吊销证书、隔离设备、阻断外联 |
建议从远程运维、云边 API、数据导出、控制指令下发这类高风险入口开始,而不是一开始就改造所有传感器和执行器。
十、常见坑与应对
| 常见坑 | 现象 | 应对 |
|---|---|---|
| 把零信任当成产品采购 | 买了网关但没有身份、策略和审计 | 先梳理访问关系和决策链路 |
| 边界思维残留 | 只按 IP 网段放行 | 引入用户、设备、资源和动作维度 |
| 权限一次性给太大 | 只读与控制指令混在一起 | 区分读、写、控制、配置和导出 |
| mTLS 后没有授权 | 任意合法证书都能访问服务 | mTLS 配合 AuthorizationPolicy 或 OPA |
| 设备证书长期不换 | 离职设备仍可接入 | 短周期证书、自动轮换、吊销机制 |
| MFA 只覆盖登录页 | 高危操作无二次确认 | 控制指令和配置变更要求 step-up |
| 策略无测试 | 修改策略后业务中断 | 策略代码化,允许 / 拒绝 / 提权用例齐备 |
| 监测数据不可解释 | 只告警“高风险” | 输出命中的规则、分数和上下文 |
| 忽略离线场景 | 断网后策略完全失效 | 网关保留必要本地策略和缓存凭据 |
| 忘记审计 | 出事后无法还原操作 | 保存请求、决策、指令和结果 |
十一、TL;DR
- 零信任的决策依据是身份、设备、资源、动作和上下文,不是 IP 网段。
- 人的身份用 OIDC + MFA,服务和设备要有独立身份与凭据。
- mTLS 解决通信双方身份与加密,授权仍需 AuthorizationPolicy 或 OPA。
- 老旧工业设备可以通过网关代理、微分段和 Topic/功能码 ACL 渐进接入。
- 最小权限要区分读、写、控制、配置和导出,高危操作要求二次认证。
- 持续验证要输出可解释的风险原因,并联动告警、提权、限权或隔离。

849

被折叠的 条评论
为什么被折叠?



