工业边缘零信任架构:从身份到持续验证的工程实战

工业边缘的网络边界正在变得模糊:设备远程运维、云边协同、移动终端接入、第三方系统集成,都会让原本“内网即可信”的假设失效。零信任架构的核心不是部署一个安全产品,而是把访问决策从“网络位置”改为“身份、设备、上下文和策略”共同决定。

本文按工程落地顺序梳理工业边缘零信任的核心原则、身份层、网络层、授权层、监测响应层,以及老设备与协议约束下的渐进式实施方法。

一、为什么工业边缘需要零信任

传统工业网络常见做法是划分为办公网、生产网、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_secretsession_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 设置 SecureHttpOnlySameSite
  • 对运维入口、控制指令、批量导出启用 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 渐进接入。
  • 最小权限要区分读、写、控制、配置和导出,高危操作要求二次认证。
  • 持续验证要输出可解释的风险原因,并联动告警、提权、限权或隔离。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值