Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- Implemented comprehensive unit tests for RabbitMqTransportServer, covering constructor, disposal, connection management, event handlers, and exception handling. - Added configuration tests for RabbitMqTransportServer to validate SSL, durable queues, auto-recovery, and custom virtual host options. - Created unit tests for UdpFrameProtocol, including frame parsing and serialization, header size validation, and round-trip data preservation. - Developed tests for UdpTransportClient, focusing on connection handling, event subscriptions, and exception scenarios. - Established tests for UdpTransportServer, ensuring proper start/stop behavior, connection state management, and event handling. - Included tests for UdpTransportOptions to verify default values and modification capabilities. - Enhanced service registration tests for Udp transport services in the dependency injection container.
301 lines
6.2 KiB
Markdown
301 lines
6.2 KiB
Markdown
# Sealed Mode Contract (AIRGAP-57)
|
|
|
|
**Contract ID:** `CONTRACT-SEALED-MODE-004`
|
|
**Version:** 1.0
|
|
**Status:** Published
|
|
**Last Updated:** 2025-12-05
|
|
|
|
## Overview
|
|
|
|
This contract defines the sealed-mode operation contract for air-gapped environments. It covers sealing/unsealing state transitions, staleness detection, time anchoring, and egress policy enforcement.
|
|
|
|
## Implementation References
|
|
|
|
- **Controller:** `src/AirGap/StellaOps.AirGap.Controller/`
|
|
- **Time:** `src/AirGap/StellaOps.AirGap.Time/`
|
|
- **Policy:** `src/AirGap/StellaOps.AirGap.Policy/`
|
|
- **Documentation:** `docs/airgap/sealing-and-egress.md`, `docs/airgap/staleness-and-time.md`
|
|
|
|
## Data Models
|
|
|
|
### AirGapState
|
|
|
|
The core sealed-mode state model.
|
|
|
|
```csharp
|
|
public sealed record AirGapState
|
|
{
|
|
public string Id { get; init; } = "singleton";
|
|
public string TenantId { get; init; } = "default";
|
|
public bool Sealed { get; init; } = false;
|
|
public string? PolicyHash { get; init; } = null;
|
|
public TimeAnchor TimeAnchor { get; init; } = TimeAnchor.Unknown;
|
|
public DateTimeOffset LastTransitionAt { get; init; }
|
|
public StalenessBudget StalenessBudget { get; init; } = StalenessBudget.Default;
|
|
}
|
|
```
|
|
|
|
### JSON Representation
|
|
|
|
```json
|
|
{
|
|
"id": "singleton",
|
|
"tenant_id": "default",
|
|
"sealed": true,
|
|
"policy_hash": "sha256:...",
|
|
"time_anchor": {
|
|
"anchor_time": "2025-12-05T10:00:00Z",
|
|
"source": "roughtime",
|
|
"format": "roughtime",
|
|
"signature_fingerprint": "...",
|
|
"token_digest": "sha256:..."
|
|
},
|
|
"last_transition_at": "2025-12-05T10:00:00Z",
|
|
"staleness_budget": {
|
|
"warning_seconds": 3600,
|
|
"breach_seconds": 7200
|
|
}
|
|
}
|
|
```
|
|
|
|
### TimeAnchor
|
|
|
|
Cryptographically verified time reference.
|
|
|
|
```json
|
|
{
|
|
"anchor_time": "2025-12-05T10:00:00Z",
|
|
"source": "roughtime|rfc3161",
|
|
"format": "roughtime|rfc3161",
|
|
"signature_fingerprint": "sha256:...",
|
|
"token_digest": "sha256:..."
|
|
}
|
|
```
|
|
|
|
### StalenessBudget
|
|
|
|
Defines staleness thresholds.
|
|
|
|
```json
|
|
{
|
|
"warning_seconds": 3600,
|
|
"breach_seconds": 7200
|
|
}
|
|
```
|
|
|
|
| Field | Default | Description |
|
|
|-------|---------|-------------|
|
|
| `warning_seconds` | 3600 | Warning threshold (1 hour) |
|
|
| `breach_seconds` | 7200 | Breach threshold (2 hours) |
|
|
|
|
### StalenessEvaluation
|
|
|
|
Result of staleness calculation.
|
|
|
|
```json
|
|
{
|
|
"age_seconds": 1800,
|
|
"warning_seconds": 3600,
|
|
"breach_seconds": 7200,
|
|
"is_breached": false,
|
|
"remaining_seconds": 1800
|
|
}
|
|
```
|
|
|
|
## API Endpoints
|
|
|
|
### Seal Environment
|
|
|
|
```
|
|
POST /system/airgap/seal
|
|
Content-Type: application/json
|
|
Authorization: Bearer <token with airgap:seal scope>
|
|
|
|
{
|
|
"policy_hash": "sha256:...",
|
|
"time_anchor": { ... },
|
|
"staleness_budget": {
|
|
"warning_seconds": 3600,
|
|
"breach_seconds": 7200
|
|
}
|
|
}
|
|
|
|
Response: 200 OK
|
|
{
|
|
"sealed": true,
|
|
"last_transition_at": "2025-12-05T10:00:00Z"
|
|
}
|
|
```
|
|
|
|
### Unseal Environment
|
|
|
|
```
|
|
POST /system/airgap/unseal
|
|
Authorization: Bearer <token with airgap:seal scope>
|
|
|
|
Response: 200 OK
|
|
{
|
|
"sealed": false,
|
|
"last_transition_at": "2025-12-05T10:00:00Z"
|
|
}
|
|
```
|
|
|
|
### Get Status
|
|
|
|
```
|
|
GET /system/airgap/status
|
|
Authorization: Bearer <token with airgap:status:read scope>
|
|
|
|
Response: 200 OK
|
|
{
|
|
"sealed": true,
|
|
"tenant_id": "default",
|
|
"staleness": {
|
|
"age_seconds": 1800,
|
|
"is_breached": false,
|
|
"remaining_seconds": 1800
|
|
},
|
|
"time_anchor": { ... },
|
|
"policy_hash": "sha256:..."
|
|
}
|
|
```
|
|
|
|
### Verify Bundle
|
|
|
|
```
|
|
POST /system/airgap/verify
|
|
Content-Type: application/json
|
|
Authorization: Bearer <token with airgap:verify scope>
|
|
|
|
{
|
|
"bundle_path": "/path/to/bundle.json",
|
|
"trust_roots_path": "/path/to/trust-roots.json"
|
|
}
|
|
|
|
Response: 200 OK
|
|
{
|
|
"valid": true,
|
|
"verification_result": {
|
|
"dsse_valid": true,
|
|
"tuf_valid": true,
|
|
"merkle_valid": true
|
|
}
|
|
}
|
|
```
|
|
|
|
## Egress Policy
|
|
|
|
### EgressPolicy Model
|
|
|
|
```csharp
|
|
public sealed class EgressPolicy
|
|
{
|
|
public EgressPolicyMode Mode { get; } // Sealed | Unsealed
|
|
public IReadOnlyList<string> AllowedHosts { get; }
|
|
public bool PermitLoopback { get; }
|
|
public bool PermitPrivateNetworks { get; }
|
|
}
|
|
```
|
|
|
|
### EgressRequest / EgressDecision
|
|
|
|
```json
|
|
// Request
|
|
{
|
|
"component": "excititor",
|
|
"destination": "https://api.github.com",
|
|
"intent": "fetch_advisories",
|
|
"operation": "GET"
|
|
}
|
|
|
|
// Decision
|
|
{
|
|
"allowed": false,
|
|
"reason": "AIRGAP_EGRESS_BLOCKED",
|
|
"remediation": "Add api.github.com to allowlist or unseal environment"
|
|
}
|
|
```
|
|
|
|
### Enforcement
|
|
|
|
When sealed:
|
|
- All outbound connections blocked by default
|
|
- Only allowlisted destinations permitted
|
|
- Loopback and private networks optionally permitted
|
|
|
|
## Time Verification
|
|
|
|
### Roughtime Verification
|
|
|
|
1. Parse Roughtime response
|
|
2. Verify Ed25519 signature against trusted public key
|
|
3. Extract anchor time from signed response
|
|
|
|
### RFC 3161 Verification
|
|
|
|
1. Parse SignedCms structure
|
|
2. Validate TSA certificate chain
|
|
3. Extract signing time from timestamp token
|
|
|
|
## Startup Diagnostics
|
|
|
|
Pre-flight checks when starting in sealed mode:
|
|
|
|
1. Verify time anchor is present
|
|
2. Check staleness budget not breached
|
|
3. Validate trust roots are loaded
|
|
4. Confirm egress policy is enforced
|
|
|
|
```
|
|
GET /healthz/ready
|
|
|
|
Response: 200 OK (if healthy)
|
|
Response: 503 Service Unavailable (if sealed mode requirements unmet)
|
|
```
|
|
|
|
## Telemetry
|
|
|
|
### Metrics
|
|
|
|
| Metric | Type | Description |
|
|
|--------|------|-------------|
|
|
| `airgap_sealed` | gauge | 1 if sealed, 0 if unsealed |
|
|
| `airgap_anchor_drift_seconds` | gauge | Seconds since time anchor |
|
|
| `airgap_anchor_expiry_seconds` | gauge | Seconds until staleness breach |
|
|
| `airgap_seal_total` | counter | Total seal operations |
|
|
| `airgap_unseal_total` | counter | Total unseal operations |
|
|
| `airgap_startup_blocked_total` | counter | Blocked startup attempts |
|
|
|
|
### Structured Logging
|
|
|
|
```json
|
|
{
|
|
"event": "airgap.sealed",
|
|
"tenant_id": "default",
|
|
"policy_hash": "sha256:...",
|
|
"timestamp": "2025-12-05T10:00:00Z"
|
|
}
|
|
```
|
|
|
|
## Authority Scopes
|
|
|
|
| Scope | Description |
|
|
|-------|-------------|
|
|
| `airgap:seal` | Seal/unseal environment |
|
|
| `airgap:status:read` | Read sealed status |
|
|
| `airgap:verify` | Verify bundles |
|
|
| `airgap:import` | Import bundles |
|
|
|
|
## Unblocks
|
|
|
|
This contract unblocks the following tasks:
|
|
|
|
- POLICY-AIRGAP-57-001
|
|
- POLICY-AIRGAP-57-002
|
|
- POLICY-AIRGAP-58-001
|
|
|
|
## Related Contracts
|
|
|
|
- [Mirror Bundle Contract](./mirror-bundle.md) - Bundle format for sealed import
|
|
- [Verification Policy Contract](./verification-policy.md) - Attestation verification
|