Skip to content

Commit 8cf5d25

Browse files
committed
feat(osep): OSEP-0011 for secure access endpoint
1 parent 6175ca4 commit 8cf5d25

1 file changed

Lines changed: 197 additions & 0 deletions

File tree

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
---
2+
title: Secure Access on GetEndpoint and Signed Endpoint
3+
authors:
4+
- "@Pangjiping"
5+
creation-date: 2026-04-19
6+
last-updated: 2026-04-19
7+
status: draft
8+
---
9+
10+
# OSEP-0011: Secure Access on GetEndpoint and Signed Endpoint
11+
12+
<!-- toc -->
13+
- [Summary](#summary)
14+
- [Goals](#goals)
15+
- [Non-Goals](#non-goals)
16+
- [Core Requirements](#core-requirements)
17+
- [Core Design](#core-design)
18+
- [1. API Changes](#1-api-changes)
19+
- [2. Signing Contract](#2-signing-contract)
20+
- [3. Server/Ingress Collaboration](#3-serveringress-collaboration)
21+
- [4. Configuration](#4-configuration)
22+
- [Validation and Error Rules](#validation-and-error-rules)
23+
- [Test Plan (Minimal)](#test-plan-minimal)
24+
- [Rollout](#rollout)
25+
<!-- /toc -->
26+
27+
## Summary
28+
29+
Add optional secure access for sandbox ingress traffic.
30+
31+
- Sandbox opt-in at create time.
32+
- `GetEndpoint` returns static auth header when enabled.
33+
- `GetSignedEndpoint(sandboxId, port, uri, timeout)` returns URL with `osb_signature`, `osb_expires`, and `osb_signed_key`.
34+
- Ingress verifies locally by decrypting `osb_signed_key` and rebuilding the signature payload.
35+
36+
## Goals
37+
38+
- Keep default behavior unchanged when security is not enabled.
39+
- Provide two access methods: static header and signed URL.
40+
- Use fast, non-reversible signing (HMAC-SHA256).
41+
- Keep ingress verification local (no per-request callback to server).
42+
43+
## Non-Goals
44+
45+
- One-time-use links.
46+
- New external dependencies.
47+
- Replacing network/TLS security controls.
48+
49+
## Core Requirements
50+
51+
1. `secure_access` is optional and default-off at sandbox level.
52+
2. `GetEndpoint` returns `OPENSANDBOX-SECURE-ACCESS` only when secure access is enabled.
53+
3. `GetSignedEndpoint` returns:
54+
- `signed_endpoint`
55+
- `osb_signature`
56+
- `osb_expires`
57+
- `osb_signed_key` (encrypted per-request signing key for ingress)
58+
4. Ingress verifies signature using canonical payload and constant-time compare.
59+
5. `osb_expires` is signed content and is also checked by ingress local time with a fixed 10-second clock skew window.
60+
61+
## Core Design
62+
63+
### 1. API Changes
64+
65+
1. **CreateSandbox**
66+
- Add `secure_access=True` (default `false`).
67+
2. **GetEndpoint**
68+
- Existing response remains.
69+
- If enabled, add header:
70+
- `OPENSANDBOX-SECURE-ACCESS: <token>`
71+
3. **GetSignedEndpoint**
72+
- New method:
73+
- `GetSignedEndpoint(sandboxId, port, uri, timeout)`
74+
- Return:
75+
- `signed_endpoint = {endpoint}{uri}?osb_signature=<sig>&osb_expires=<epoch>&osb_signed_key=<cipher>`
76+
77+
### 2. Signing Contract
78+
79+
Algorithm:
80+
81+
- HMAC-SHA256
82+
- base64url (no padding)
83+
- constant-time compare
84+
85+
Canonical payload:
86+
87+
- Static token:
88+
- `v1\nstatic\n{sandbox_id}\n{port}`
89+
- Signed URL:
90+
- `v1\nsigned\n{sandbox_id}\n{port}\n{normalized_uri}\n{osb_expires}`
91+
92+
Rules:
93+
94+
- `normalized_uri` excludes `osb_signature`.
95+
- URI normalization must be identical in server and ingress.
96+
- `osb_expires` is included in the signature payload.
97+
- `osb_signed_key` carries an encrypted per-request HMAC key and is required for ingress verification.
98+
- Signature flow for signed URL:
99+
1. server generates per-request key `k_req`
100+
2. `osb_signature = HMAC_SHA256(k_req, canonical_payload)`
101+
3. `osb_signed_key = EncryptForIngress(k_req, wrap_key_id)`
102+
4. ingress decrypts `osb_signed_key` to recover `k_req` and verifies signature
103+
104+
### 3. Server/Ingress Collaboration
105+
106+
Control plane:
107+
108+
1. Same wrap-key set is distributed to server and ingress.
109+
2. Server sets one active wrap key for encrypting per-request signing keys.
110+
3. Ingress loads unwrap keys for decrypting `osb_signed_key`.
111+
112+
Request flow:
113+
114+
1. Server signs in `GetEndpoint` / `GetSignedEndpoint`.
115+
2. Client sends request to ingress.
116+
3. Ingress reconstructs canonical payload and verifies signature locally.
117+
4. Ingress checks expiration after signature verification:
118+
- accept when `now <= osb_expires + 10s`
119+
- reject otherwise
120+
5. Pass on success; reject on verification failure.
121+
122+
Key rotation:
123+
124+
1. Add new key to both sides.
125+
2. Switch server active wrap key.
126+
3. Keep old key in ingress unwrap set during grace window.
127+
4. Remove old key after rollout window.
128+
129+
### 4. Configuration
130+
131+
Server uses TOML and keeps this capability under `[ingress]`:
132+
133+
```toml
134+
[ingress]
135+
mode = "gateway" # or "direct"
136+
137+
[ingress.secure_access]
138+
enabled = true
139+
active_wrap_key_id = "wk1"
140+
max_signed_timeout_seconds = 3600
141+
142+
[[ingress.secure_access.wrap_keys]]
143+
key_id = "wk1"
144+
secret = "base64:..."
145+
status = "active-wrap"
146+
147+
[[ingress.secure_access.wrap_keys]]
148+
key_id = "wk0"
149+
secret = "base64:..."
150+
status = "unwrap-only"
151+
```
152+
153+
Ingress uses CLI flags (same style as existing ingress flags):
154+
155+
```bash
156+
opensandbox-ingress \
157+
--secure-access-enabled \
158+
--secure-access-unwrap-keys "wk1=base64:...,wk0=base64:..." \
159+
--secure-access-active-unwrap-key-ids "wk1,wk0"
160+
```
161+
162+
## Validation and Error Rules
163+
164+
- If secure access is disabled for sandbox: keep existing behavior.
165+
- If enabled, request must satisfy one mode:
166+
- valid static header token, or
167+
- valid signed URL signature.
168+
- `401` on signature/key/canonicalization mismatch.
169+
- `401` when `osb_signed_key` cannot be decrypted by ingress unwrap keys.
170+
- `401` on signed URL expiration (`now > osb_expires + 10s`).
171+
- `400` on malformed signed parameters (missing required fields, invalid format).
172+
- `GetSignedEndpoint`:
173+
- `404` sandbox not found
174+
- `403` secure access not enabled
175+
- `400` invalid `uri` or `timeout`
176+
177+
## Test Plan (Minimal)
178+
179+
- Unit:
180+
- canonicalization parity
181+
- signature generation/verification
182+
- constant-time compare path
183+
- Integration:
184+
- `GetEndpoint` returns secure header only when enabled
185+
- `GetSignedEndpoint` generates expected fields
186+
- ingress accepts valid signature and rejects tampered payload
187+
- ingress rejects request when `osb_signed_key` decrypt fails
188+
- ingress rejects expired signed URL and accepts within 10-second skew window
189+
- Cross-component:
190+
- fixed test vectors for `(sandbox_id, port, uri, osb_expires, k_req, osb_signed_key, expected_sig)`
191+
192+
## Rollout
193+
194+
1. Deploy with secure access config present but disabled.
195+
2. Enable ingress verification path.
196+
3. Enable secure access for canary sandboxes.
197+
4. Expand gradually and monitor 401/verify-fail metrics.

0 commit comments

Comments
 (0)