EnerOS v0.20.0
Release Date: June 1, 2025 Codename: ZeroTrust Git Tag: v0.20.0 Support Status: Stable Total Crates: 56 (4 new) Test Cases: 8020+ (540 new)
Version Overview
EnerOS v0.20.0 “ZeroTrust” is the security capstone release for EnerOS’s “Internal Iteration” phase (v0.1.0 to v0.39.0). The primary goal is to introduce a Zero Trust Architecture, including mTLS mutual authentication, service mesh, automatic certificate rotation, and network policies, enabling EnerOS clusters to implement a “never trust, always verify” security model across three layers: inter-node communication, inter-service invocation, and client access, meeting NERC CIP, IEC 62443, and other power industry security compliance requirements.
The traditional security model adopts a “castle and moat” approach—internal networks are considered trusted, with protection only at the external perimeter. Once an attacker breaches the perimeter, they can move laterally within the internal network. As national critical information infrastructure, power systems face multiple risks including APT attacks, supply chain attacks, and internal threats—traditional perimeter protection can no longer meet requirements. Zero Trust Architecture assumes “the network is always untrusted,” with all communication requiring mutual authentication, least privilege, and continuous verification, fundamentally eliminating internal trust assumptions.
This version introduces four new crates: eneros-trust (zero trust core), eneros-trust-mtls (mTLS mutual authentication), eneros-trust-mesh (service mesh), and eneros-trust-cert (certificate management). The design philosophy is “identity as perimeter, encryption as default”—all services and nodes possess X.509 certificate identities, all communication is mTLS-encrypted by default, access control is identity-based rather than network-location-based, and certificate lifecycle is fully automated.
Key Metrics
| Metric | v0.19.0 | v0.20.0 | Improvement |
|---|---|---|---|
| Communication encryption coverage | 30% | 100% | Full encryption |
| Certificate rotation automation | Manual | Fully automatic | Zero ops |
| Inter-service authentication | One-way | Mutual mTLS | Strong identity |
| Network policy granularity | IP-level | Service-level | Fine-grained |
| Compliance rate | 80% | 99% | +19pp |
New Features
1. Zero Trust Architecture
Introduced the eneros-trust crate as the unified entry point for the zero trust subsystem, coordinating identity authentication, access control, communication encryption, and policy enforcement. The zero trust architecture follows the NIST SP 800-207 standard, implementing the “Subject → Resource → Policy” access model.
Zero Trust Model
use eneros_trust::{ZeroTrust, TrustConfig, Identity, Policy};
let trust = ZeroTrust::new(TrustConfig {
mtls_required: true,
cert_authority: CertAuthority::internal("https://ca.internal:8200"),
policy_engine: PolicyEngine::opa(),
audit_all_access: true,
default_deny: true,
})?;
// Register service identity
trust.register_identity(Identity::service("eneros-scheduler")
.namespace("default")
.permissions(&["topology:read", "dispatch:command"]))?;
// Enable zero trust
trust.enable().await?;
Zero Trust Principles
| Principle | Implementation | Description |
|---|---|---|
| Never trust | Default deny | All access denied by default |
| Always verify | Per-request authentication | No cached trust |
| Least privilege | RBAC + ABAC | Only necessary permissions granted |
| Continuous verification | Real-time policy evaluation | Policy changes take effect immediately |
| Encrypted communication | Full mTLS coverage | All traffic encrypted |
| Assume breach | Comprehensive auditing | All access recorded |
2. mTLS Mutual Authentication
Introduced the eneros-trust-mtls crate, enforcing mTLS mutual authentication for all EnerOS internal communication (inter-node, inter-service, client-server). Both communicating parties must present valid X.509 certificates, with certificates issued by an internal CA and identity based on the certificate SAN (Subject Alternative Name) field.
mTLS Configuration
use eneros_trust_mtls::{MtlsConfig, MtlsManager};
let mtls = MtlsManager::new(MtlsConfig {
ca_cert: "/etc/eneros/certs/ca.pem",
server_cert: "/etc/eneros/certs/server.pem",
server_key: "/etc/eneros/certs/server.key",
client_cert: "/etc/eneros/certs/client.pem",
client_key: "/etc/eneros/certs/client.key",
min_tls_version: TlsVersion::V1_3,
cipher_suites: CipherSuite::modern(),
cert_rotation: RotationConfig::auto(Duration::days(30)),
})?;
// Apply to all gRPC / HTTP servers
ctx.apply_mtls(&mtls).await?;
// Apply to all outbound connections
ctx.apply_mtls_client(&mtls).await?;
Certificate Identity Model
use eneros_trust_mtls::{CertificateIdentity, SpiffeId};
// Service identity based on SPIFFE ID
let identity = CertificateIdentity::from_spiffe(
SpiffeId::new("spiffe://eneros/default/scheduler")
)?;
// Extract identity from certificate
fn verify_peer(cert: &X509) -> Result<Identity> {
let spiffe = cert.spiffe_id()?;
let identity = Identity::from_spiffe(spiffe)?;
// Verify certificate validity
cert.verify_chain(&ca_cert)?;
cert.verify_not_expired()?;
cert.verify_not_revoked()?;
Ok(identity)
}
mTLS Handshake Performance
| Phase | Duration | Description |
|---|---|---|
| TCP handshake | 0.5ms | Basic connection |
| TLS handshake | 1.2ms | Including certificate verification |
| Session resumption | 0.3ms | 0-RTT |
| Total (new connection) | 1.7ms | Acceptable |
| Total (reused) | 0.3ms | Very low |
3. Service Mesh
Introduced the eneros-trust-mesh crate, implementing a lightweight service mesh providing transparent mTLS encryption, load balancing, circuit breaking and rate limiting, and observability for inter-service communication within EnerOS. The service mesh uses the Sidecar pattern, transparent to applications without requiring business code modifications.
Service Mesh Architecture
use eneros_trust_mesh::{ServiceMesh, MeshConfig};
let mesh = ServiceMesh::new(MeshConfig {
sidecar_image: "eneros/mesh-sidecar:0.20",
control_plane: "mesh-control:15000",
data_plane_port: 15001,
mtls_enabled: true,
auto_injection: true,
tracing: true,
metrics: true,
})?;
// Register services
mesh.register_service(Service::new("eneros-scheduler")
.port(8080)
.namespace("default")
.mtls_required(true))?;
mesh.register_service(Service::new("eneros-twin")
.port(8081)
.namespace("default")
.mtls_required(true))?;
mesh.start().await?;
Service Mesh Capabilities
| Capability | Description | Transparency |
|---|---|---|
| mTLS encryption | Auto-encrypt inter-service communication | Fully transparent |
| Load balancing | Multi-instance request distribution | Fully transparent |
| Circuit breaking | Automatic isolation of failed instances | Fully transparent |
| Rate limiting | Service-level rate limiting | Configuration only |
| Retry | Automatic retry of failed requests | Configuration only |
| Tracing | Full-chain tracing | Fully transparent |
| Metrics | Service-level metric exposure | Fully transparent |
Traffic Policy
use eneros_trust_mesh::{TrafficPolicy, Rule};
// Define traffic policy
let policy = TrafficPolicy::new("scheduler-to-twin")
.source("eneros-scheduler")
.destination("eneros-twin")
.mtls(MtlsMode::Strict) // Enforce mTLS
.load_balancing(LoadBalancing::round_robin())
.circuit_breaker(CircuitBreaker::new()
.error_threshold(0.1)
.consecutive_errors(5)
.cooldown(Duration::seconds(30)))
.rate_limit(RateLimit::new(1000, Duration::seconds(1)))
.retry(RetryPolicy::new()
.max_attempts(3)
.backoff(Backoff::exponential(Duration::milliseconds(50))));
mesh.apply_policy(policy).await?;
4. Automatic Certificate Rotation
Introduced the eneros-trust-cert crate, implementing full lifecycle automation for X.509 certificates, including issuance, distribution, rotation, and revocation. Certificate rotation requires no service restart, achieving zero-downtime updates through hot reloading.
Certificate Management
use eneros_trust_cert::{CertManager, CertConfig, CertSpec};
let cert_manager = CertManager::new(CertConfig {
ca_endpoint: "https://ca.internal:8200",
ca_token: env::var("CA_TOKEN")?,
storage: CertStorage::disk("/etc/eneros/certs"),
rotation_threshold: Duration::days(7), // Rotate 7 days before expiry
rotation_check: Duration::hours(1),
})?;
// Issue certificate for service
let cert = cert_manager.issue(CertSpec::new("eneros-scheduler")
.namespace("default")
.san_dns("scheduler.eneros.svc")
.san_spiffe("spiffe://eneros/default/scheduler")
.validity(Duration::days(90))
.key_type(KeyType::EcdsaP256)
.key_usage(&[KeyUsage::DigitalSignature, KeyUsage::KeyEncipherment]))?;
Automatic Rotation Flow
// Certificate rotation monitoring
cert_manager.on_rotation(|event| {
log::info!("Certificate rotation: service={}", event.service);
match event.phase {
RotationPhase::Started => {
log::info!("Starting certificate rotation for {}", event.service);
}
RotationPhase::NewCertIssued => {
log::info!("New certificate issued, starting hot reload");
}
RotationPhase::HotReloaded => {
log::info!("Certificate hot reloaded, new connections use new certificate");
}
RotationPhase::Completed => {
log::info!("Old certificate revoked, rotation completed");
}
}
});
// Start automatic rotation
cert_manager.start_auto_rotation().await?;
Certificate Lifecycle
| Phase | Trigger | Duration | Impact |
|---|---|---|---|
| Issuance | Service registration | 500ms | New certificate |
| Distribution | After issuance | 100ms | Certificate to node |
| Rotation | 7 days before expiry | 2s | Zero downtime |
| Hot reload | After rotation | 200ms | New connections use new cert |
| Revocation | 24h after rotation | 100ms | Old certificate invalidated |
Certificate Revocation Check
use eneros_trust_cert::{RevocationList, OcspResponder};
// Configure revocation check
let revocation = RevocationList::new()
.ocsp_responder(OcspResponder::new("http://ocsp.internal"))
.crl_check(CrlCheck::enabled())
.cache(Duration::minutes(5));
// Check revocation during mTLS handshake
mtls.with_revocation_check(revocation)?;
5. Network Policies
Introduced identity-based network policies, replacing traditional IP-based firewall rules. Network policies use service identity as granularity, declaratively defining allowed communication paths, denying all traffic not explicitly allowed by default.
Network Policy Definition
use eneros_trust::network::{NetworkPolicy, Rule, Direction};
let policy = NetworkPolicy::new("scheduler-allow-list")
.namespace("default")
.pod_selector(PodSelector::service("eneros-scheduler"))
.ingress(Rule::allow()
.from(IdentitySelector::service("eneros-gateway"))
.to_ports(&[Port::tcp(8080)]))
.ingress(Rule::allow()
.from(IdentitySelector::service("eneros-dashboard"))
.to_ports(&[Port::tcp(8080)]))
.egress(Rule::allow()
.to(IdentitySelector::service("eneros-twin"))
.to_ports(&[Port::tcp(8081)]))
.egress(Rule::allow()
.to(IdentitySelector::service("eneros-timeseries"))
.to_ports(&[Port::tcp(9090)]))
.default_deny(Direction::Both);
trust.apply_network_policy(policy).await?;
Network Policy Example Table
| Source Service | Target Service | Port | Protocol | Action |
|---|---|---|---|---|
| eneros-gateway | eneros-scheduler | 8080 | TCP | Allow |
| eneros-dashboard | eneros-scheduler | 8080 | TCP | Allow |
| eneros-scheduler | eneros-twin | 8081 | TCP | Allow |
| eneros-scheduler | eneros-timeseries | 9090 | TCP | Allow |
| * | eneros-scheduler | * | * | Deny |
Policy Audit
use eneros_trust::audit::PolicyAudit;
let audit = PolicyAudit::new(&trust);
// Query denied connections
let denied = audit.query()
.action(Action::Deny)
.range(now() - Duration::hours(24), now())
.execute()?;
for entry in denied {
println!("{:?} {} -> {} denied: {}",
entry.timestamp, entry.source, entry.destination, entry.reason);
}
// Generate compliance report
let report = audit.compliance_report(Standard::NercCip)
.period(Q2_2025)
.generate()?;
Improvements
- API Gateway: All REST/GraphQL endpoints enforce mTLS, removing HTTP plaintext support
- Raft Cluster: Inter-node communication enables mTLS, preventing unauthorized nodes from joining
- SCADA: IEC 104 and IEC 61850 connections support TLS encapsulation
- Dashboard: Frontend communicates with backend via mTLS, WebSocket over TLS
Bug Fixes
- Fixed
eneros-trust-mtlsoccasional handshake failure during certificate rotation (#2008) - Fixed
eneros-trust-meshSidecar injection memory leak under high concurrency (#2014) - Fixed
eneros-trust-certOCSP response cache expiration not refreshing (#2020) - Fixed
eneros-trustnetwork policy brief full-access window during policy updates (#2026)
Breaking Changes
- All HTTP endpoints enable mTLS by default: Original HTTP endpoints migrated to HTTPS, requiring client certificate configuration
RaftNode::new: Newtls_configparameter, inter-node communication requires mTLSGateway::listen: Removed plaintext listening, unified tolisten_tls
Performance Improvements
- mTLS handshake latency stable at 1.7ms (new connection) / 0.3ms (session resumption)
- Certificate rotation zero-downtime, hot reload in 200ms
- Service mesh Sidecar additional latency < 0.5ms
- Network policy evaluation latency < 50μs
Contributors
This version was jointly completed by 27 contributors with 468 commits. Special thanks to:
- @zero-trust-arch: Zero trust architecture and policy engine
- @mtls-implementer: mTLS mutual authentication and TLS 1.3
- @mesh-builder: Service mesh and Sidecar design
- @cert-automation: Certificate automatic rotation and hot reload
Upgrade Guide
Upgrading from v0.19.0
1. Update Dependencies
# Cargo.toml
[dependencies]
eneros-trust = { version = "0.20" }
eneros-trust-mtls = { version = "0.20" }
eneros-trust-mesh = { version = "0.20" }
eneros-trust-cert = { version = "0.20" }
2. Deploy Internal CA
# Deploy internal certificate authority
eneros-ca init --endpoint https://ca.internal:8200
eneros-ca issue-root --validity 3650
eneros-ca issue-intermediate --validity 365
3. Enable Zero Trust
# eneros.toml
[trust]
enabled = true
mtls_required = true
default_deny = true
[trust.ca]
endpoint = "https://ca.internal:8200"
token = "${CA_TOKEN}"
[trust.cert]
rotation_threshold_days = 7
rotation_check_hours = 1
key_type = "ecdsa-p256"
[trust.mesh]
enabled = true
auto_injection = true
[trust.network_policy]
enabled = true
default_deny = "both"
4. Migrate Clients to mTLS
// v0.19.0 old approach: plaintext HTTP
let client = HttpClient::new("http://eneros-scheduler:8080")?;
// v0.20.0 new approach: mTLS HTTPS
let client = HttpClient::builder("https://eneros-scheduler:8080")
.mtls(MtlsConfig::load("/etc/eneros/certs")?)
.build()?;
5. Configure Network Policies
Recommend adopting a “whitelist” approach—first allow necessary communication, then deny all others by default:
// Allow business-critical communication
trust.apply_network_policy(NetworkPolicy::new("business-allow")
.ingress(Rule::allow().from(IdentitySelector::service("eneros-gateway")))
.egress(Rule::allow().to(IdentitySelector::service("eneros-twin")))
.egress(Rule::allow().to(IdentitySelector::service("eneros-timeseries")))
.default_deny(Direction::Both))?;
// Default deny
trust.apply_network_policy(NetworkPolicy::new("default-deny")
.default_deny(Direction::Both))?;
6. Compliance Verification
After enabling zero trust, recommend performing compliance verification to ensure NERC CIP and IEC 62443 requirements are met:
eneros-trust verify --standard nerc-cip
eneros-trust verify --standard iec-62443
eneros-trust report --period 2025-Q2 --output compliance.pdf
Zero Trust is the security capstone for EnerOS’s internal iteration phase (v0.1.0 to v0.39.0). With this, EnerOS has completed full-stack capability building from underlying architecture to upper-layer security, laying a solid foundation for the v0.40.0 first public release.