安全守卫
EnerOS 安全守卫是 Agent 决策落地前的最后一道防线,位于内核态的命令执行通道上。所有来自 Agent、API、外部系统的控制命令都必须通过安全网关的拦截与校验,确保即便上层逻辑出现缺陷也不会引发电力系统事故。
电力系统是 cyber-physical 系统,一次错误命令可能造成大面积停电、设备损毁甚至人身事故。EnerOS 的安全守卫基于「纵深防御」原则构建多层防线,单点失效不会导致事故穿透到物理电网。这是 EnerOS 区别于通用 AgentOS 的安全护城河。
架构总览
┌─────────────────────────────────────────────────────┐
│ Agent / API / 外部系统 │
└──────────────────┬──────────────────────────────────┘
│ submit(cmd)
┌──────────────────┴──────────────────────────────────┐
│ SafetyGateway (责任链) │
│ ┌──────────────────────────────────────────────┐ │
│ │ 1. AuthStage mTLS / OAuth2 / mTLS+Token │ │
│ │ 2. PermissionStage RBAC / ABAC │ │
│ │ 3. ConstraintStage 严格约束校验 │ │
│ │ 4. RuleStage 电力安全规则 │ │
│ │ 5. RateLimitStage 速率与配额 │ │
│ │ 6. AuditStage WORM 审计 │ │
│ └──────────────────────────────────────────────┘ │
│ ↓ Verdict │
│ Allow / Deny / Project │
└──────────────────┬──────────────────────────────────┘
│ execute
┌──────────────────┴──────────────────────────────────┐
│ 设备执行层(实时域) │
└─────────────────────────────────────────────────────┘
安全网关架构
网关采用责任链模式,每条命令依次通过:身份认证 → 权限校验 → 约束校验 → 安全规则 → 速率限制 → 审计记录。
use eneros_safety::{SafetyGateway, Pipeline};
use std::time::Duration;
let gateway = SafetyGateway::builder()
.stage(AuthStage::mtls())
.stage(PermissionStage::rbac())
.stage(ConstraintStage::strict())
.stage(RuleStage::default())
.stage(RateLimitStage::new(100, Duration::from_secs(1)))
.stage(AuditStage::worm())
.build();
责任链阶段说明
| 阶段 | 职责 | 失败动作 | 典型耗时 |
|---|
| AuthStage | 身份认证(mTLS / Token) | Deny | < 50μs |
| PermissionStage | 权限校验(RBAC / ABAC) | Deny | < 20μs |
| ConstraintStage | 物理约束校验 | Deny / Project | < 200μs |
| RuleStage | 电力安全规则(五防等) | Deny / Project | < 30μs |
| RateLimitStage | 速率与配额 | Deny | < 5μs |
| AuditStage | WORM 审计写入 | 不阻断 | < 50μs |
| 总计 | - | - | < 100μs |
命令拦截
每条命令通过 submit 进入网关,返回 Allow / Deny / Project:
use eneros_safety::{Command, Verdict, BreakerId};
let cmd = Command::OpenBreaker { id: breaker_id };
match gateway.submit(cmd).await? {
Verdict::Allow => {
ctx.execute(cmd).await?;
}
Verdict::Deny(reason) => {
log::warn!("命令被拦截: {}", reason);
ctx.notify_agent("命令被安全网关拒绝").await?;
}
Verdict::Project(alternative) => {
log::info!("命令被投影为可行解: {:?}", alternative);
ctx.execute(alternative).await?;
}
}
Command 变体
| 变体 | 含义 | 风险等级 |
|---|
| OpenBreaker { id } | 断开断路器 | 高 |
| CloseBreaker { id } | 合上断路器 | 高 |
| OpenDisconnector { id } | 拉开刀闸 | 高 |
| CloseDisconnector { id } | 合上刀闸 | 中 |
| SetGenerator { id, mw } | 调整机组出力 | 中 |
| AdjustTap { id, tap } | 调节分接头 | 低 |
| SetLoad { id, mw } | 调整负荷 | 中 |
| GroundDevice { id } | 装设接地线 | 高 |
| EmergencyStop | 紧急停机 | 极高 |
Verdict 变体
| 变体 | 含义 | 处理 |
|---|
| Allow | 全部通过 | 执行 |
| Deny(reason) | 被拒绝 | 通知 Agent |
| Project(alt) | 投影为可行解 | 执行 alt |
| Delay(duration) | 延迟执行 | 等待后重试 |
| ManualReview | 需人工审核 | 转人工 |
安全规则
规则系统支持组合式策略,覆盖典型电力安全场景:
use eneros_safety::{Rule, RuleSet};
let rules = RuleSet::new()
// 禁止带负荷拉刀闸
.add(Rule::no_load_break_disconnector())
// 同期检定
.add(Rule::sync_check_before_close())
// 五防闭锁
.add(Rule::five_interlocks())
// 检修区域隔离
.add(Rule::maintenance_zone_isolation())
// 频率异常闭锁
.add(Rule::frequency_block(49.0, 51.0))
// 电压越限闭锁
.add(Rule::voltage_block(0.85, 1.15))
// 自定义规则
.add(Rule::custom("no_dispatch_during_maintenance", |cmd, ctx| {
if ctx.maintenance_in_progress() && cmd.is_dispatch() {
Verdict::Deny("检修期间禁止调度操作".into())
} else {
Verdict::Allow
}
}));
内置规则一览
| 规则 | 检查内容 | 适用设备 |
|---|
| no_load_break_disconnector | 禁止带负荷拉刀闸 | 刀闸 |
| sync_check_before_close | 合闸前同期检定 | 断路器 |
| five_interlocks | 五防闭锁全检 | 所有设备 |
| maintenance_zone_isolation | 检修区域隔离 | 所有设备 |
| frequency_block | 频率越限闭锁 | 系统级 |
| voltage_block | 电压越限闭锁 | 母线级 |
| overload_block | 设备过载闭锁 | 线路、变压器 |
| cold_load_pickup | 冷负荷投入限制 | 负荷开关 |
| reclosing_block | 重合闸闭锁 | 断路器 |
| parallel_protection | 并列保护 | 发电机 |
操作票
对于复杂操作序列,强制走操作票流程,确保顺序与隔离:
use eneros_safety::{OperationTicket, Step};
let ticket = OperationTicket::new()
.step(Step::open(bus_id_1))
.step(Step::verify_open(bus_id_1))
.step(Step::ground(bus_id_1))
.step(Step::maintain(bus_id_1))
.step(Step::unground(bus_id_1))
.step(Step::close(bus_id_1))
.step(Step::verify_closed(bus_id_1));
let ticket_id = gateway.submit_ticket(ticket).await?;
// 必须按序执行,跳步会被拒绝
// 每步执行后自动记录到 WORM 审计
// 查询操作票状态
let status = gateway.ticket_status(ticket_id).await?;
println!("当前步骤: {}/{}", status.current_step, status.total_steps);
OperationTicket 字段
| 字段 | 类型 | 说明 |
|---|
| id | TicketId | 操作票唯一 ID |
| steps | Vec | 操作步骤序列 |
| created_by | AgentId | 创建者 |
| approved_by | Option | 审批人(关键操作) |
| status | TicketStatus | Draft / Pending / Active / Done / Cancelled |
| timeout | Duration | 超时自动取消 |
速率与配额
防止 Agent 失控导致的「命令风暴」:
// 单 Agent 速率限制
gateway.set_rate_limit("dispatch_agent", 100, Duration::from_secs(1));
// 关键设备配额
gateway.set_quota("circuit_breaker_5", 5, Duration::from_hours(1));
// 突发流量桶
gateway.set_burst_limit("protection_agent", 10, 100, Duration::from_secs(1));
// 全局速率
gateway.set_global_rate_limit(10000, Duration::from_secs(1));
速率策略
| 策略 | 含义 | 适用场景 |
|---|
| TokenBucket | 令牌桶 | 一般 Agent |
| LeakyBucket | 漏桶 | 平滑流量 |
| SlidingWindow | 滑动窗口 | 精确限流 |
| Quota | 总量配额 | 关键设备 |
审计与 WORM
所有命令决策(含被拦截的)写入 WORM(Write Once Read Many)存储,满足 NERC CIP 与 IEC 62443 取证要求:
use eneros_safety::{AuditQuery, Range, VerdictFilter};
let audit_log = gateway.audit_log();
// 查询审计
let entries = audit_log.query(
AuditQuery::new()
.agent("dispatch_agent")
.range(Range::last(Duration::from_hours(24)))
.verdict(VerdictFilter::All)
).await?;
for entry in &entries {
println!("{} | {:?} | {} | {:?}",
entry.timestamp, entry.verdict, entry.command, entry.agent);
}
// 导出审计报告(合规)
let report = audit_log.export_report(
Range::between(month_start, month_end),
Format::Csv,
).await?;
std::fs::write("audit_202607.csv", report)?;
AuditEntry 字段
| 字段 | 类型 | 说明 |
|---|
| timestamp | Timestamp | 命令提交时间 |
| agent | AgentId | 提交者 |
| command | Command | 命令内容 |
| verdict | Verdict | 处置结果 |
| reason | Option | 拒绝原因 |
| duration_us | u64 | 校验耗时(μs) |
| tenant | TenantId | 租户 |
| signature | [u8; 32] | 防篡改签名 |
紧急停机
支持硬件级紧急停机按钮,绕过所有 Agent 直接断开:
use eneros_safety::EmergencyScope;
gateway.emergency_stop(EmergencyScope::All).await?;
// 内核自动完成:
// 1. 所有 Agent 暂停
// 2. 所有控制输出置零
// 3. 进入安全状态
// 4. 通知运维人员
// 5. 启动应急流程
// 局部紧急停机
gateway.emergency_stop(EmergencyScope::Region("substation_a")).await?;
// 恢复运行(需人工确认)
gateway.resume_from_emergency().await?;
EmergencyScope 变体
| 变体 | 范围 | 恢复方式 |
|---|
| All | 全系统 | 人工 |
| Region(name) | 指定区域 | 人工 |
| Agent(id) | 单个 Agent | 自动 |
| Device(id) | 单个设备 | 自动 |
安全等级矩阵
| 等级 | 描述 | 阶段 | 适用场景 |
|---|
| L0 | 全开放 | 仅审计 | 实验室测试 |
| L1 | 约束校验 | Auth + Constraint | 数字孪生 |
| L2 | 约束+规则 | + Rule + RateLimit | 生产环境 |
| L3 | 全链路+操作票 | + Permission + Ticket | 关键基础设施 |
| L4 | L3+独立硬件校验 | + Hardware Safety Module | 国家级电网 |
use eneros_safety::SafetyLevel;
// 配置安全等级
gateway.set_level(SafetyLevel::L3)?;
// 运行时动态切换(仅可降级到更严格)
gateway.escalate_to(SafetyLevel::L4).await?;
性能指标
| 操作 | 延迟 | 吞吐 |
|---|
| 单命令校验(全链路) | < 100μs | 10k cmd/s |
| AuthStage | < 50μs | - |
| PermissionStage | < 20μs | - |
| ConstraintStage(1000 节点) | < 200μs | - |
| RuleStage | < 30μs | - |
| 操作票执行(5 步) | < 1ms | - |
| WORM 审计写入 | < 50μs | 100k entries/s |
| 紧急停机响应 | < 1ms | - |
| 审计查询(24h) | < 50ms | - |
测试环境:4 核 / 8GB / Ubuntu 22.04。
配置参数
eneros.toml 中安全相关配置:
[safety]
# 安全等级:L0 / L1 / L2 / L3 / L4
level = "L3"
# 是否强制操作票
require_ticket = true
# 默认速率限制(cmd/s)
default_rate_limit = 100
# 关键设备配额(cmd/h)
critical_device_quota = 5
# WORM 审计存储路径
audit_dir = "/var/eneros/audit"
# 审计保留期(天)
audit_retention_days = 3650
# 是否启用紧急停机硬件接口
hardware_emergency_stop = true
# 紧急停机心跳超时(ms)
emergency_heartbeat_ms = 100
| 参数 | 类型 | 默认值 | 说明 |
|---|
| level | enum | L3 | 安全等级 |
| require_ticket | bool | true | 强制操作票 |
| default_rate_limit | u32 | 100 | 默认速率限制 |
| critical_device_quota | u32 | 5 | 关键设备配额 |
| audit_dir | String | /var/eneros/audit | WORM 审计路径 |
| audit_retention_days | u32 | 3650 | 审计保留期 |
| hardware_emergency_stop | bool | true | 硬件紧急停机 |
| emergency_heartbeat_ms | u32 | 100 | 心跳超时 |
与其他能力的关系
| 关联能力 | 互动方式 |
|---|
| 物理约束决策 | 约束引擎是网关核心 |
| 零信任与安全增强 | 身份认证与审计 |
| 多智能体协作 | Agent 命令的来源 |
| 实时双执行域 | 实时域命令也经过网关 |
| 多租户与隔离 | 命令按租户命名空间审计 |
| 时序原生操作 | 审计日志入库 |
| 电网拓扑一等公民 | 操作票依赖拓扑校验 |
相关文档