@bluefly/openstandardagents 0.2.8 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +1 -1
- package/.github/AGENTS.md +245 -0
- package/.github/agents/github-issue-triage.ossa.yaml +99 -0
- package/.github/agents/github-pr-triage.ossa.yaml +137 -0
- package/.github/workflows/issue-sync-to-gitlab.yml +138 -0
- package/.github/workflows/pr-triage-to-gitlab.yml +164 -0
- package/.version.json +1 -1
- package/.wiki-config.json +1 -1
- package/CHANGELOG.md +33 -0
- package/CONTRIBUTING.md +102 -3
- package/README.md +17 -10
- package/dist/services/release-automation/schemas/release.schema.js +1 -1
- package/dist/services/release-automation/webhook.service.js +3 -3
- package/dist/services/release-automation/webhook.service.js.map +1 -1
- package/dist/services/runtime/claude/claude-adapter.d.ts +1 -1
- package/dist/services/runtime/claude/claude-adapter.d.ts.map +1 -1
- package/dist/services/runtime/claude/claude-adapter.js +1 -1
- package/dist/services/runtime/claude/claude-adapter.js.map +1 -1
- package/dist/spec/v0.2.9/a2a-protocol.md +1337 -0
- package/dist/spec/v0.2.9/agent.md +1946 -0
- package/dist/spec/v0.2.9/capabilities/index.yaml +25 -0
- package/dist/spec/v0.2.9/capabilities/memory.yaml +251 -0
- package/dist/spec/v0.2.9/capability-schema.md +576 -0
- package/dist/spec/v0.2.9/compliance-profiles.md +533 -0
- package/dist/spec/v0.2.9/conformance-testing.md +1527 -0
- package/dist/spec/v0.2.9/gitlab-duo-integration.md +621 -0
- package/dist/spec/v0.2.9/ossa-0.2.9.schema.json +3699 -0
- package/dist/spec/v0.2.9/runtime-semantics.md +464 -0
- package/dist/spec/v0.2.9/security-model.md +1245 -0
- package/dist/spec/v0.2.9/semantic-conventions.md +347 -0
- package/dist/spec/v0.2.9/types.ts +522 -0
- package/dist/types/policy.d.ts +377 -0
- package/dist/types/policy.d.ts.map +1 -0
- package/dist/types/policy.js +84 -0
- package/dist/types/policy.js.map +1 -0
- package/dist/utils/version.js +1 -1
- package/docs/specs/policy-dsl.md +925 -0
- package/examples/adk-integration/code-review-workflow.yml +1 -1
- package/examples/adk-integration/customer-support.yml +1 -1
- package/examples/adk-integration/data-pipeline.yml +1 -1
- package/examples/advanced/reasoning-agent.yaml +136 -0
- package/examples/advanced/workflows/hybrid-model-strategy.yaml +1 -1
- package/examples/agent-manifests/critics/critic-agent.yaml +1 -1
- package/examples/agent-manifests/governors/governor-agent.yaml +1 -1
- package/examples/agent-manifests/integrators/integrator-agent.yaml +1 -1
- package/examples/agent-manifests/judges/judge-agent.yaml +1 -1
- package/examples/agent-manifests/monitors/monitor-agent.yaml +1 -1
- package/examples/agent-manifests/orchestrators/orchestrator-agent.yaml +1 -1
- package/examples/agent-manifests/sample-compliant-agent.yaml +1 -1
- package/examples/agent-manifests/workers/worker-agent.yaml +1 -1
- package/examples/agents-md/code-agent.ossa.json +100 -0
- package/examples/agents-md/monorepo-agent.ossa.yaml +180 -0
- package/examples/anthropic/claude-assistant.ossa.json +1 -1
- package/examples/autogen/multi-agent.ossa.json +1 -1
- package/examples/claude-code/code-reviewer.ossa.yaml +1 -1
- package/examples/claude-code/ossa-validator.ossa.yaml +2 -2
- package/examples/common_npm/agent-router.ossa.yaml +1 -1
- package/examples/common_npm/agent-router.v0.2.2.ossa.yaml +1 -1
- package/examples/crewai/research-team.ossa.json +1 -1
- package/examples/cursor/code-review-agent.ossa.json +1 -1
- package/examples/drupal/gitlab-ml-recommender.ossa.yaml +1 -1
- package/examples/drupal/gitlab-ml-recommender.v0.2.2.ossa.yaml +1 -1
- package/examples/extensions/agents-md-v1.yml +175 -0
- package/examples/extensions/drupal-v1.yml +1 -1
- package/examples/extensions/kagent-v1.yml +1 -1
- package/examples/getting-started/hello-world-complete.ossa.yaml +1 -1
- package/examples/integration-patterns/agent-to-agent-orchestration.ossa.yaml +4 -4
- package/examples/kagent/compliance-validator.ossa.yaml +1 -1
- package/examples/kagent/cost-optimizer.ossa.yaml +1 -1
- package/examples/kagent/documentation-agent.ossa.yaml +1 -1
- package/examples/kagent/k8s-troubleshooter-v1.ossa.yaml +1 -1
- package/examples/kagent/k8s-troubleshooter-v1.v0.2.2.ossa.yaml +1 -1
- package/examples/kagent/k8s-troubleshooter.ossa.yaml +1 -1
- package/examples/kagent/security-scanner.ossa.yaml +1 -1
- package/examples/langchain/chain-agent.ossa.json +1 -1
- package/examples/langflow/workflow-agent.ossa.json +1 -1
- package/examples/langgraph/state-machine-agent.ossa.json +1 -1
- package/examples/llamaindex/rag-agent.ossa.json +1 -1
- package/examples/migration-guides/from-langchain-to-ossa.yaml +4 -4
- package/examples/multi-agent/conditional-router.ossa.yaml +1 -1
- package/examples/multi-agent/parallel-execution.ossa.yaml +1 -1
- package/examples/multi-agent/sequential-pipeline.ossa.yaml +1 -1
- package/examples/openai/basic-agent.ossa.yaml +1 -1
- package/examples/openai/multi-tool-agent.ossa.json +1 -1
- package/examples/openai/swarm-agent.ossa.json +1 -1
- package/examples/production/document-analyzer-openai.yml +1 -1
- package/examples/quickstart/support-agent.ossa.yaml +1 -1
- package/examples/templates/ossa-compliance.yaml +1 -1
- package/examples/vercel/edge-agent.ossa.json +1 -1
- package/llms.txt +1 -1
- package/package.json +5 -3
- package/scripts/README.md +25 -0
- package/scripts/compliance-audit.ts +796 -0
- package/scripts/generate-agents-catalog.ts +2 -1
- package/scripts/generate-api-docs.ts +2 -1
- package/scripts/generate-examples-docs.ts +2 -1
- package/scripts/generate-llms-ctx.sh +2 -2
- package/spec/v0.2.9/a2a-protocol.md +1337 -0
- package/spec/v0.2.9/agent.md +1946 -0
- package/spec/v0.2.9/capabilities/index.yaml +25 -0
- package/spec/v0.2.9/capabilities/memory.yaml +251 -0
- package/spec/v0.2.9/capability-schema.md +576 -0
- package/spec/v0.2.9/compliance-profiles.md +533 -0
- package/spec/v0.2.9/conformance-testing.md +1527 -0
- package/spec/v0.2.9/gitlab-duo-integration.md +621 -0
- package/spec/v0.2.9/ossa-0.2.9.schema.json +3699 -0
- package/spec/v0.2.9/runtime-semantics.md +464 -0
- package/spec/v0.2.9/security-model.md +1245 -0
- package/spec/v0.2.9/semantic-conventions.md +347 -0
- package/spec/v0.2.9/types.ts +522 -0
- package/test-results/junit.xml +184 -146
- package/.github/workflows/pr-comment.yml +0 -33
|
@@ -0,0 +1,1245 @@
|
|
|
1
|
+
# OSSA Security Model
|
|
2
|
+
|
|
3
|
+
**Version**: 0.2.9
|
|
4
|
+
**Status**: Draft
|
|
5
|
+
**Last Updated**: 2025-12-04
|
|
6
|
+
|
|
7
|
+
This document defines the security model for OSSA-compliant agents, including identity management, authentication, authorization, secrets handling, sandboxing, and audit logging.
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
The OSSA security model operates on **deny-by-default** principles with defense-in-depth:
|
|
12
|
+
|
|
13
|
+
1. **Identity**: Every agent has a cryptographically verifiable identity
|
|
14
|
+
2. **Authentication**: Agents authenticate using mTLS, JWT, or OIDC
|
|
15
|
+
3. **Authorization**: RBAC/ABAC policies control agent capabilities
|
|
16
|
+
4. **Secrets**: Never embedded in manifests; always externalized
|
|
17
|
+
5. **Sandboxing**: Agents run in isolated environments with resource limits
|
|
18
|
+
6. **Audit**: All security events are logged in OpenTelemetry-compatible format
|
|
19
|
+
|
|
20
|
+
```yaml
|
|
21
|
+
apiVersion: ossa/v0.2.9
|
|
22
|
+
kind: Agent
|
|
23
|
+
metadata:
|
|
24
|
+
name: secure-agent
|
|
25
|
+
spec:
|
|
26
|
+
security:
|
|
27
|
+
identity:
|
|
28
|
+
urn: ossa:agent:acme:secure-agent:1.0.0
|
|
29
|
+
attestation:
|
|
30
|
+
type: x509
|
|
31
|
+
certificate_ref: ${vault:pki/agent-cert}
|
|
32
|
+
|
|
33
|
+
authentication:
|
|
34
|
+
methods:
|
|
35
|
+
- mtls
|
|
36
|
+
- jwt
|
|
37
|
+
jwt:
|
|
38
|
+
issuer: https://auth.acme.com
|
|
39
|
+
audience: ossa-runtime
|
|
40
|
+
|
|
41
|
+
authorization:
|
|
42
|
+
rbac:
|
|
43
|
+
role: worker
|
|
44
|
+
policies:
|
|
45
|
+
- allow: tools.read
|
|
46
|
+
- deny: tools.delete
|
|
47
|
+
|
|
48
|
+
sandbox:
|
|
49
|
+
isolation: container
|
|
50
|
+
resources:
|
|
51
|
+
memory: 512Mi
|
|
52
|
+
cpu: 1000m
|
|
53
|
+
network:
|
|
54
|
+
egress:
|
|
55
|
+
- https://api.github.com
|
|
56
|
+
- https://api.gitlab.com
|
|
57
|
+
|
|
58
|
+
audit:
|
|
59
|
+
enabled: true
|
|
60
|
+
events:
|
|
61
|
+
- agent_started
|
|
62
|
+
- capability_invoked
|
|
63
|
+
- secret_accessed
|
|
64
|
+
- policy_violation
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Agent Identity
|
|
70
|
+
|
|
71
|
+
Every OSSA agent MUST have a globally unique identity expressed as a URN.
|
|
72
|
+
|
|
73
|
+
### URN Format
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
ossa:agent:<organization>:<name>:<version>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Examples**:
|
|
80
|
+
- `ossa:agent:acme:code-reviewer:1.2.0`
|
|
81
|
+
- `ossa:agent:github:security-scanner:2.0.0`
|
|
82
|
+
- `ossa:agent:gitlab:merge-approver:3.1.0`
|
|
83
|
+
|
|
84
|
+
### Identity Schema
|
|
85
|
+
|
|
86
|
+
```yaml
|
|
87
|
+
spec:
|
|
88
|
+
security:
|
|
89
|
+
identity:
|
|
90
|
+
urn: string # Required: Agent URN
|
|
91
|
+
attestation:
|
|
92
|
+
type: x509 | jwt | spiffe # Required: Attestation type
|
|
93
|
+
certificate_ref?: string # For x509/SPIFFE
|
|
94
|
+
jwt_ref?: string # For JWT
|
|
95
|
+
trust_anchor: string # Trust root CA/issuer
|
|
96
|
+
|
|
97
|
+
labels: # Optional: Identity labels
|
|
98
|
+
organization: string
|
|
99
|
+
team: string
|
|
100
|
+
environment: dev | staging | prod
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Attestation Types
|
|
104
|
+
|
|
105
|
+
#### X.509 Certificate Attestation
|
|
106
|
+
|
|
107
|
+
Agents present X.509 certificates signed by a trusted CA.
|
|
108
|
+
|
|
109
|
+
```yaml
|
|
110
|
+
attestation:
|
|
111
|
+
type: x509
|
|
112
|
+
certificate_ref: ${vault:pki/certs/agent-cert}
|
|
113
|
+
trust_anchor: ${vault:pki/ca/root-ca}
|
|
114
|
+
|
|
115
|
+
# Optional: Certificate validation rules
|
|
116
|
+
validation:
|
|
117
|
+
require_san: true
|
|
118
|
+
allowed_sans:
|
|
119
|
+
- DNS:secure-agent.acme.com
|
|
120
|
+
- URI:ossa:agent:acme:secure-agent:1.0.0
|
|
121
|
+
check_revocation: true
|
|
122
|
+
ocsp_endpoints:
|
|
123
|
+
- https://ocsp.acme.com
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Trust Chain**:
|
|
127
|
+
1. Runtime validates certificate against trust anchor
|
|
128
|
+
2. Checks SAN matches agent URN
|
|
129
|
+
3. Verifies certificate not revoked (OCSP/CRL)
|
|
130
|
+
4. Extracts identity from certificate Subject/SAN
|
|
131
|
+
|
|
132
|
+
#### JWT Attestation
|
|
133
|
+
|
|
134
|
+
Agents present JWT tokens with OSSA-specific claims.
|
|
135
|
+
|
|
136
|
+
```yaml
|
|
137
|
+
attestation:
|
|
138
|
+
type: jwt
|
|
139
|
+
jwt_ref: ${env:OSSA_AGENT_TOKEN}
|
|
140
|
+
trust_anchor: https://auth.acme.com/.well-known/jwks.json
|
|
141
|
+
|
|
142
|
+
# Required JWT claims
|
|
143
|
+
claims:
|
|
144
|
+
iss: https://auth.acme.com
|
|
145
|
+
sub: ossa:agent:acme:secure-agent:1.0.0
|
|
146
|
+
aud: ossa-runtime
|
|
147
|
+
ossa.agent.id: secure-agent
|
|
148
|
+
ossa.agent.version: 1.0.0
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**JWT Structure**:
|
|
152
|
+
```json
|
|
153
|
+
{
|
|
154
|
+
"iss": "https://auth.acme.com",
|
|
155
|
+
"sub": "ossa:agent:acme:secure-agent:1.0.0",
|
|
156
|
+
"aud": "ossa-runtime",
|
|
157
|
+
"exp": 1735747200,
|
|
158
|
+
"iat": 1735660800,
|
|
159
|
+
"ossa.agent.id": "secure-agent",
|
|
160
|
+
"ossa.agent.version": "1.0.0",
|
|
161
|
+
"ossa.agent.capabilities": ["code_review", "security_scan"]
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
#### SPIFFE SVID Attestation
|
|
166
|
+
|
|
167
|
+
Agents use SPIFFE Verifiable Identity Documents for zero-trust environments.
|
|
168
|
+
|
|
169
|
+
```yaml
|
|
170
|
+
attestation:
|
|
171
|
+
type: spiffe
|
|
172
|
+
spiffe_id: spiffe://acme.com/agent/secure-agent
|
|
173
|
+
trust_bundle_ref: ${vault:spiffe/bundles/acme}
|
|
174
|
+
|
|
175
|
+
workload_api:
|
|
176
|
+
socket_path: unix:///run/spire/agent.sock
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**SPIFFE Integration**:
|
|
180
|
+
- Agent retrieves X.509-SVID from SPIRE Workload API
|
|
181
|
+
- SVID contains SPIFFE ID matching agent URN
|
|
182
|
+
- Runtime validates against trust bundle
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Authentication Methods
|
|
187
|
+
|
|
188
|
+
OSSA supports multiple authentication methods for different deployment scenarios.
|
|
189
|
+
|
|
190
|
+
### mTLS (Mutual TLS)
|
|
191
|
+
|
|
192
|
+
**Use Case**: Service-to-service authentication, Kubernetes, SPIFFE
|
|
193
|
+
|
|
194
|
+
```yaml
|
|
195
|
+
authentication:
|
|
196
|
+
methods:
|
|
197
|
+
- mtls
|
|
198
|
+
|
|
199
|
+
mtls:
|
|
200
|
+
client_certificate_ref: ${vault:pki/agent-cert}
|
|
201
|
+
client_key_ref: ${vault:pki/agent-key}
|
|
202
|
+
ca_bundle_ref: ${vault:pki/ca-bundle}
|
|
203
|
+
|
|
204
|
+
# Optional: TLS configuration
|
|
205
|
+
min_tls_version: "1.3"
|
|
206
|
+
cipher_suites:
|
|
207
|
+
- TLS_AES_256_GCM_SHA384
|
|
208
|
+
- TLS_CHACHA20_POLY1305_SHA256
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**Workflow**:
|
|
212
|
+
1. Agent presents client certificate during TLS handshake
|
|
213
|
+
2. Runtime validates certificate against CA bundle
|
|
214
|
+
3. Runtime extracts identity from certificate Subject/SAN
|
|
215
|
+
4. Connection established with mutual authentication
|
|
216
|
+
|
|
217
|
+
### Bearer Token (JWT)
|
|
218
|
+
|
|
219
|
+
**Use Case**: API authentication, token-based flows
|
|
220
|
+
|
|
221
|
+
```yaml
|
|
222
|
+
authentication:
|
|
223
|
+
methods:
|
|
224
|
+
- jwt
|
|
225
|
+
|
|
226
|
+
jwt:
|
|
227
|
+
token_ref: ${vault:secret/agent-jwt}
|
|
228
|
+
issuer: https://auth.acme.com
|
|
229
|
+
audience: ossa-runtime
|
|
230
|
+
|
|
231
|
+
# Optional: Token validation
|
|
232
|
+
validation:
|
|
233
|
+
require_expiration: true
|
|
234
|
+
clock_skew_seconds: 300
|
|
235
|
+
required_claims:
|
|
236
|
+
- ossa.agent.id
|
|
237
|
+
- ossa.agent.version
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
**Token Lifecycle**:
|
|
241
|
+
- **Issue**: Auth server issues JWT with OSSA claims
|
|
242
|
+
- **Present**: Agent includes JWT in `Authorization: Bearer <token>` header
|
|
243
|
+
- **Validate**: Runtime validates signature, expiration, claims
|
|
244
|
+
- **Rotate**: Agents refresh tokens before expiration
|
|
245
|
+
|
|
246
|
+
### OIDC (OpenID Connect)
|
|
247
|
+
|
|
248
|
+
**Use Case**: Federated identity, SSO integration
|
|
249
|
+
|
|
250
|
+
```yaml
|
|
251
|
+
authentication:
|
|
252
|
+
methods:
|
|
253
|
+
- oidc
|
|
254
|
+
|
|
255
|
+
oidc:
|
|
256
|
+
issuer: https://auth.acme.com
|
|
257
|
+
client_id: ossa-agent-secure-agent
|
|
258
|
+
client_secret_ref: ${vault:secret/oidc-client-secret}
|
|
259
|
+
|
|
260
|
+
scopes:
|
|
261
|
+
- openid
|
|
262
|
+
- profile
|
|
263
|
+
- ossa:agent
|
|
264
|
+
|
|
265
|
+
# Optional: Discovery endpoint override
|
|
266
|
+
discovery_url: https://auth.acme.com/.well-known/openid-configuration
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
**Flow**:
|
|
270
|
+
1. Agent initiates OIDC authorization code flow
|
|
271
|
+
2. User authenticates (if interactive) or client credentials grant
|
|
272
|
+
3. Agent exchanges code for ID token + access token
|
|
273
|
+
4. Runtime validates ID token signature and claims
|
|
274
|
+
|
|
275
|
+
### API Key
|
|
276
|
+
|
|
277
|
+
**Use Case**: Simple authentication, dev/testing environments
|
|
278
|
+
|
|
279
|
+
```yaml
|
|
280
|
+
authentication:
|
|
281
|
+
methods:
|
|
282
|
+
- api_key
|
|
283
|
+
|
|
284
|
+
api_key:
|
|
285
|
+
key_ref: ${vault:secret/api-key}
|
|
286
|
+
header_name: X-OSSA-API-Key
|
|
287
|
+
|
|
288
|
+
# Optional: Key properties
|
|
289
|
+
scopes:
|
|
290
|
+
- tools.read
|
|
291
|
+
- tools.execute
|
|
292
|
+
rate_limit:
|
|
293
|
+
requests_per_minute: 100
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
**Security Considerations**:
|
|
297
|
+
- API keys MUST be scoped to specific capabilities
|
|
298
|
+
- API keys MUST be rotatable without downtime
|
|
299
|
+
- API keys SHOULD have expiration dates
|
|
300
|
+
- API keys MUST NOT be logged in plaintext
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Authorization Model
|
|
305
|
+
|
|
306
|
+
OSSA uses **Role-Based Access Control (RBAC)** with **Attribute-Based Access Control (ABAC)** extensions.
|
|
307
|
+
|
|
308
|
+
### RBAC Roles
|
|
309
|
+
|
|
310
|
+
#### Orchestrator
|
|
311
|
+
|
|
312
|
+
**Capabilities**: Full control over agent lifecycle and delegation
|
|
313
|
+
|
|
314
|
+
```yaml
|
|
315
|
+
authorization:
|
|
316
|
+
rbac:
|
|
317
|
+
role: orchestrator
|
|
318
|
+
|
|
319
|
+
permissions:
|
|
320
|
+
- agents.create
|
|
321
|
+
- agents.delete
|
|
322
|
+
- agents.delegate
|
|
323
|
+
- tools.*
|
|
324
|
+
- state.read
|
|
325
|
+
- state.write
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
**Use Case**: Multi-agent orchestrators, workflow engines
|
|
329
|
+
|
|
330
|
+
#### Worker
|
|
331
|
+
|
|
332
|
+
**Capabilities**: Execute assigned tasks, read state, invoke tools
|
|
333
|
+
|
|
334
|
+
```yaml
|
|
335
|
+
authorization:
|
|
336
|
+
rbac:
|
|
337
|
+
role: worker
|
|
338
|
+
|
|
339
|
+
permissions:
|
|
340
|
+
- tools.read
|
|
341
|
+
- tools.execute
|
|
342
|
+
- state.read
|
|
343
|
+
- state.write # Own state only
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
**Use Case**: Specialized agents, task executors
|
|
347
|
+
|
|
348
|
+
#### Auditor
|
|
349
|
+
|
|
350
|
+
**Capabilities**: Read-only access for compliance and monitoring
|
|
351
|
+
|
|
352
|
+
```yaml
|
|
353
|
+
authorization:
|
|
354
|
+
rbac:
|
|
355
|
+
role: auditor
|
|
356
|
+
|
|
357
|
+
permissions:
|
|
358
|
+
- tools.read
|
|
359
|
+
- state.read
|
|
360
|
+
- audit.read
|
|
361
|
+
- metrics.read
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
**Use Case**: Compliance agents, monitoring dashboards
|
|
365
|
+
|
|
366
|
+
### ABAC Attributes
|
|
367
|
+
|
|
368
|
+
Extend RBAC with context-aware policies.
|
|
369
|
+
|
|
370
|
+
```yaml
|
|
371
|
+
authorization:
|
|
372
|
+
rbac:
|
|
373
|
+
role: worker
|
|
374
|
+
|
|
375
|
+
abac:
|
|
376
|
+
attributes:
|
|
377
|
+
- name: environment
|
|
378
|
+
value: production
|
|
379
|
+
- name: region
|
|
380
|
+
value: us-east-1
|
|
381
|
+
- name: compliance
|
|
382
|
+
value: hipaa
|
|
383
|
+
|
|
384
|
+
policies:
|
|
385
|
+
- condition: environment == "production"
|
|
386
|
+
effect: deny
|
|
387
|
+
actions:
|
|
388
|
+
- tools.delete
|
|
389
|
+
|
|
390
|
+
- condition: compliance == "hipaa" && tool.type == "external_api"
|
|
391
|
+
effect: allow
|
|
392
|
+
actions:
|
|
393
|
+
- tools.execute
|
|
394
|
+
require_audit: true
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Policy Enforcement Points
|
|
398
|
+
|
|
399
|
+
Policies are enforced at multiple points:
|
|
400
|
+
|
|
401
|
+
1. **Startup**: Validate agent has required permissions
|
|
402
|
+
2. **Runtime**: Check policy before each operation
|
|
403
|
+
3. **Tool Invocation**: Enforce per-tool policies
|
|
404
|
+
4. **State Access**: Validate read/write permissions
|
|
405
|
+
|
|
406
|
+
```typescript
|
|
407
|
+
interface PolicyEnforcement {
|
|
408
|
+
// Called before agent initialization
|
|
409
|
+
validateStartup(manifest: AgentManifest): PolicyResult;
|
|
410
|
+
|
|
411
|
+
// Called before each operation
|
|
412
|
+
enforcePolicy(
|
|
413
|
+
operation: Operation,
|
|
414
|
+
context: SecurityContext
|
|
415
|
+
): boolean;
|
|
416
|
+
|
|
417
|
+
// Called before tool invocation
|
|
418
|
+
checkToolPolicy(
|
|
419
|
+
tool: string,
|
|
420
|
+
action: string,
|
|
421
|
+
context: SecurityContext
|
|
422
|
+
): boolean;
|
|
423
|
+
|
|
424
|
+
// Called before state access
|
|
425
|
+
checkStatePolicy(
|
|
426
|
+
key: string,
|
|
427
|
+
operation: 'read' | 'write',
|
|
428
|
+
context: SecurityContext
|
|
429
|
+
): boolean;
|
|
430
|
+
}
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### Deny-by-Default
|
|
434
|
+
|
|
435
|
+
**CRITICAL**: OSSA runtimes MUST implement deny-by-default policies.
|
|
436
|
+
|
|
437
|
+
- If no policy allows an operation, it is **DENIED**
|
|
438
|
+
- Explicit deny ALWAYS overrides allow
|
|
439
|
+
- Permissions are additive (multiple roles accumulate)
|
|
440
|
+
|
|
441
|
+
```yaml
|
|
442
|
+
# Example: Default deny policy
|
|
443
|
+
authorization:
|
|
444
|
+
default_policy: deny
|
|
445
|
+
|
|
446
|
+
policies:
|
|
447
|
+
- effect: allow
|
|
448
|
+
actions:
|
|
449
|
+
- tools.read
|
|
450
|
+
resources:
|
|
451
|
+
- gitlab-api
|
|
452
|
+
- github-api
|
|
453
|
+
|
|
454
|
+
- effect: deny
|
|
455
|
+
actions:
|
|
456
|
+
- tools.delete
|
|
457
|
+
resources:
|
|
458
|
+
- "*" # Deny all deletions
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
---
|
|
462
|
+
|
|
463
|
+
## Secrets Management
|
|
464
|
+
|
|
465
|
+
**ABSOLUTE REQUIREMENT**: Secrets MUST NEVER be embedded in agent manifests.
|
|
466
|
+
|
|
467
|
+
### Prohibited
|
|
468
|
+
|
|
469
|
+
```yaml
|
|
470
|
+
# ❌ NEVER DO THIS
|
|
471
|
+
spec:
|
|
472
|
+
tools:
|
|
473
|
+
- name: gitlab-api
|
|
474
|
+
type: http
|
|
475
|
+
config:
|
|
476
|
+
api_key: glpat-supersecretkey123 # NEVER embed secrets!
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
### Required
|
|
480
|
+
|
|
481
|
+
```yaml
|
|
482
|
+
# ✅ ALWAYS DO THIS
|
|
483
|
+
spec:
|
|
484
|
+
tools:
|
|
485
|
+
- name: gitlab-api
|
|
486
|
+
type: http
|
|
487
|
+
config:
|
|
488
|
+
api_key_ref: ${vault:secret/gitlab/api-key}
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
### Secret Reference Syntax
|
|
492
|
+
|
|
493
|
+
Secrets are referenced using provider-specific URIs:
|
|
494
|
+
|
|
495
|
+
```
|
|
496
|
+
${<provider>:<path>[:<key>][?<options>]}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
**Examples**:
|
|
500
|
+
- `${vault:secret/data/gitlab/api-key}`
|
|
501
|
+
- `${k8s:secrets/ossa-secrets:gitlab-token}`
|
|
502
|
+
- `${env:GITLAB_API_KEY}`
|
|
503
|
+
- `${aws:secretsmanager/gitlab-api-key}`
|
|
504
|
+
- `${azure:keyvault/gitlab-api-key}`
|
|
505
|
+
|
|
506
|
+
### Storage Options
|
|
507
|
+
|
|
508
|
+
#### HashiCorp Vault
|
|
509
|
+
|
|
510
|
+
```yaml
|
|
511
|
+
secrets:
|
|
512
|
+
provider: vault
|
|
513
|
+
config:
|
|
514
|
+
address: https://vault.acme.com
|
|
515
|
+
namespace: ossa-agents
|
|
516
|
+
auth:
|
|
517
|
+
method: kubernetes
|
|
518
|
+
role: ossa-agent
|
|
519
|
+
|
|
520
|
+
# Optional: TLS configuration
|
|
521
|
+
tls:
|
|
522
|
+
ca_cert_ref: ${file:/etc/vault/ca.crt}
|
|
523
|
+
client_cert_ref: ${file:/etc/vault/client.crt}
|
|
524
|
+
client_key_ref: ${file:/etc/vault/client.key}
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
**Reference Syntax**:
|
|
528
|
+
- KV v2: `${vault:secret/data/path/to/secret:key}`
|
|
529
|
+
- PKI: `${vault:pki/issue/agent-role:certificate}`
|
|
530
|
+
- Database: `${vault:database/creds/readonly:password}`
|
|
531
|
+
|
|
532
|
+
#### Kubernetes Secrets
|
|
533
|
+
|
|
534
|
+
```yaml
|
|
535
|
+
secrets:
|
|
536
|
+
provider: kubernetes
|
|
537
|
+
config:
|
|
538
|
+
namespace: ossa-agents
|
|
539
|
+
service_account: ossa-agent-sa
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
**Reference Syntax**:
|
|
543
|
+
- `${k8s:secrets/secret-name:key-name}`
|
|
544
|
+
- `${k8s:configmaps/config-name:key-name}`
|
|
545
|
+
|
|
546
|
+
#### Environment Variables
|
|
547
|
+
|
|
548
|
+
```yaml
|
|
549
|
+
secrets:
|
|
550
|
+
provider: env
|
|
551
|
+
config:
|
|
552
|
+
allowed_prefixes:
|
|
553
|
+
- OSSA_
|
|
554
|
+
- AGENT_
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
**Reference Syntax**:
|
|
558
|
+
- `${env:OSSA_API_KEY}`
|
|
559
|
+
- `${env:AGENT_SECRET_TOKEN}`
|
|
560
|
+
|
|
561
|
+
**Security Note**: Environment variables are acceptable for dev/testing but NOT recommended for production.
|
|
562
|
+
|
|
563
|
+
#### AWS Secrets Manager
|
|
564
|
+
|
|
565
|
+
```yaml
|
|
566
|
+
secrets:
|
|
567
|
+
provider: aws
|
|
568
|
+
config:
|
|
569
|
+
region: us-east-1
|
|
570
|
+
auth:
|
|
571
|
+
method: iam_role
|
|
572
|
+
role_arn: arn:aws:iam::123456789012:role/ossa-agent
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
**Reference Syntax**:
|
|
576
|
+
- `${aws:secretsmanager/secret-name}`
|
|
577
|
+
- `${aws:secretsmanager/secret-name:version-id}`
|
|
578
|
+
|
|
579
|
+
#### Azure Key Vault
|
|
580
|
+
|
|
581
|
+
```yaml
|
|
582
|
+
secrets:
|
|
583
|
+
provider: azure
|
|
584
|
+
config:
|
|
585
|
+
vault_url: https://ossa-vault.vault.azure.net
|
|
586
|
+
auth:
|
|
587
|
+
method: managed_identity
|
|
588
|
+
client_id: 12345678-1234-1234-1234-123456789012
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
**Reference Syntax**:
|
|
592
|
+
- `${azure:keyvault/secret-name}`
|
|
593
|
+
- `${azure:keyvault/secret-name:version}`
|
|
594
|
+
|
|
595
|
+
### Secret Rotation Policy
|
|
596
|
+
|
|
597
|
+
Secrets MUST support rotation without agent restart.
|
|
598
|
+
|
|
599
|
+
```yaml
|
|
600
|
+
secrets:
|
|
601
|
+
rotation:
|
|
602
|
+
enabled: true
|
|
603
|
+
check_interval: 300s # Check every 5 minutes
|
|
604
|
+
|
|
605
|
+
# Optional: Rotation notifications
|
|
606
|
+
notification:
|
|
607
|
+
webhook: https://alerts.acme.com/secret-rotated
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
**Rotation Flow**:
|
|
611
|
+
1. Secret updated in provider (Vault, K8s, etc.)
|
|
612
|
+
2. Agent detects change (polling or webhook)
|
|
613
|
+
3. Agent reloads secret value
|
|
614
|
+
4. Agent uses new secret for subsequent operations
|
|
615
|
+
5. Old secret remains valid for grace period
|
|
616
|
+
|
|
617
|
+
### Secret Lifecycle Events
|
|
618
|
+
|
|
619
|
+
Agents MUST emit audit events for secret operations:
|
|
620
|
+
|
|
621
|
+
```yaml
|
|
622
|
+
audit:
|
|
623
|
+
events:
|
|
624
|
+
- secret_accessed # Secret read from provider
|
|
625
|
+
- secret_rotated # Secret value changed
|
|
626
|
+
- secret_access_denied # Secret access failed
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
---
|
|
630
|
+
|
|
631
|
+
## Sandboxing Requirements
|
|
632
|
+
|
|
633
|
+
Agents MUST run in isolated execution environments with resource limits.
|
|
634
|
+
|
|
635
|
+
### Isolation Levels
|
|
636
|
+
|
|
637
|
+
#### Process Isolation
|
|
638
|
+
|
|
639
|
+
**Minimum viable isolation** for dev/testing.
|
|
640
|
+
|
|
641
|
+
```yaml
|
|
642
|
+
sandbox:
|
|
643
|
+
isolation: process
|
|
644
|
+
|
|
645
|
+
resources:
|
|
646
|
+
memory: 256Mi
|
|
647
|
+
cpu: 500m
|
|
648
|
+
|
|
649
|
+
# Process-level restrictions
|
|
650
|
+
restrictions:
|
|
651
|
+
read_only_filesystem: false
|
|
652
|
+
allow_network: true
|
|
653
|
+
allow_ipc: false
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
**Mechanisms**: Process namespaces, resource limits (cgroups)
|
|
657
|
+
|
|
658
|
+
#### Container Isolation
|
|
659
|
+
|
|
660
|
+
**Recommended for production** deployments.
|
|
661
|
+
|
|
662
|
+
```yaml
|
|
663
|
+
sandbox:
|
|
664
|
+
isolation: container
|
|
665
|
+
|
|
666
|
+
resources:
|
|
667
|
+
memory: 512Mi
|
|
668
|
+
cpu: 1000m
|
|
669
|
+
ephemeral_storage: 1Gi
|
|
670
|
+
|
|
671
|
+
container:
|
|
672
|
+
image: ossa-runtime:latest
|
|
673
|
+
read_only_root: true
|
|
674
|
+
allow_privilege_escalation: false
|
|
675
|
+
run_as_non_root: true
|
|
676
|
+
run_as_user: 1000
|
|
677
|
+
|
|
678
|
+
seccomp_profile: runtime/default
|
|
679
|
+
apparmor_profile: ossa-agent
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
**Mechanisms**: Docker, containerd, Podman
|
|
683
|
+
|
|
684
|
+
#### VM Isolation
|
|
685
|
+
|
|
686
|
+
**Maximum security** for untrusted agents.
|
|
687
|
+
|
|
688
|
+
```yaml
|
|
689
|
+
sandbox:
|
|
690
|
+
isolation: vm
|
|
691
|
+
|
|
692
|
+
resources:
|
|
693
|
+
memory: 2Gi
|
|
694
|
+
cpu: 2000m
|
|
695
|
+
disk: 10Gi
|
|
696
|
+
|
|
697
|
+
vm:
|
|
698
|
+
hypervisor: firecracker
|
|
699
|
+
kernel: vmlinux-5.10
|
|
700
|
+
init: /sbin/init
|
|
701
|
+
|
|
702
|
+
# Firecracker-specific config
|
|
703
|
+
firecracker:
|
|
704
|
+
vsock: true
|
|
705
|
+
balloon: true
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
**Mechanisms**: Firecracker, gVisor, Kata Containers
|
|
709
|
+
|
|
710
|
+
### Resource Limits
|
|
711
|
+
|
|
712
|
+
All isolation levels MUST enforce resource limits.
|
|
713
|
+
|
|
714
|
+
```yaml
|
|
715
|
+
sandbox:
|
|
716
|
+
resources:
|
|
717
|
+
memory: 512Mi # Max memory
|
|
718
|
+
cpu: 1000m # Max CPU (1 core)
|
|
719
|
+
ephemeral_storage: 1Gi # Max disk usage
|
|
720
|
+
|
|
721
|
+
# Optional: Request vs. limit (Kubernetes-style)
|
|
722
|
+
requests:
|
|
723
|
+
memory: 256Mi
|
|
724
|
+
cpu: 500m
|
|
725
|
+
|
|
726
|
+
limits:
|
|
727
|
+
memory: 512Mi
|
|
728
|
+
cpu: 1000m
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
**Enforcement**:
|
|
732
|
+
- **Memory**: OOM kill if exceeded
|
|
733
|
+
- **CPU**: Throttling if exceeded
|
|
734
|
+
- **Disk**: Write failure if exceeded
|
|
735
|
+
|
|
736
|
+
### Syscall Filtering
|
|
737
|
+
|
|
738
|
+
Restrict syscalls available to agent processes.
|
|
739
|
+
|
|
740
|
+
```yaml
|
|
741
|
+
sandbox:
|
|
742
|
+
syscall_filter:
|
|
743
|
+
default_action: SCMP_ACT_ERRNO
|
|
744
|
+
|
|
745
|
+
allowed_syscalls:
|
|
746
|
+
- read
|
|
747
|
+
- write
|
|
748
|
+
- open
|
|
749
|
+
- close
|
|
750
|
+
- stat
|
|
751
|
+
- fstat
|
|
752
|
+
- lstat
|
|
753
|
+
- poll
|
|
754
|
+
- mmap
|
|
755
|
+
- munmap
|
|
756
|
+
- brk
|
|
757
|
+
- rt_sigaction
|
|
758
|
+
- rt_sigprocmask
|
|
759
|
+
- ioctl
|
|
760
|
+
- socket
|
|
761
|
+
- connect
|
|
762
|
+
- sendto
|
|
763
|
+
- recvfrom
|
|
764
|
+
|
|
765
|
+
denied_syscalls:
|
|
766
|
+
- ptrace # Prevent debugging
|
|
767
|
+
- reboot # Prevent system reboot
|
|
768
|
+
- kexec_load # Prevent kernel loading
|
|
769
|
+
```
|
|
770
|
+
|
|
771
|
+
**Mechanisms**: seccomp-bpf, AppArmor, SELinux
|
|
772
|
+
|
|
773
|
+
### Network Policy
|
|
774
|
+
|
|
775
|
+
Control network access with egress/ingress rules.
|
|
776
|
+
|
|
777
|
+
```yaml
|
|
778
|
+
sandbox:
|
|
779
|
+
network:
|
|
780
|
+
# Egress rules (outbound)
|
|
781
|
+
egress:
|
|
782
|
+
- protocol: https
|
|
783
|
+
destinations:
|
|
784
|
+
- api.github.com
|
|
785
|
+
- api.gitlab.com
|
|
786
|
+
- registry.npmjs.org
|
|
787
|
+
ports:
|
|
788
|
+
- 443
|
|
789
|
+
|
|
790
|
+
- protocol: http
|
|
791
|
+
destinations:
|
|
792
|
+
- internal-api.acme.com
|
|
793
|
+
ports:
|
|
794
|
+
- 8080
|
|
795
|
+
|
|
796
|
+
# Ingress rules (inbound)
|
|
797
|
+
ingress:
|
|
798
|
+
- protocol: http
|
|
799
|
+
sources:
|
|
800
|
+
- 10.0.0.0/8 # Internal network
|
|
801
|
+
ports:
|
|
802
|
+
- 8080
|
|
803
|
+
|
|
804
|
+
# Default deny
|
|
805
|
+
default_policy: deny
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
**Enforcement**:
|
|
809
|
+
- **iptables**: Linux firewall rules
|
|
810
|
+
- **Network Policies**: Kubernetes NetworkPolicy
|
|
811
|
+
- **DNS filtering**: Allowed domains only
|
|
812
|
+
|
|
813
|
+
### Filesystem Restrictions
|
|
814
|
+
|
|
815
|
+
Control filesystem access with read/write permissions.
|
|
816
|
+
|
|
817
|
+
```yaml
|
|
818
|
+
sandbox:
|
|
819
|
+
filesystem:
|
|
820
|
+
read_only_paths:
|
|
821
|
+
- /usr
|
|
822
|
+
- /lib
|
|
823
|
+
- /lib64
|
|
824
|
+
- /etc
|
|
825
|
+
|
|
826
|
+
read_write_paths:
|
|
827
|
+
- /tmp
|
|
828
|
+
- /var/tmp
|
|
829
|
+
- /workspace
|
|
830
|
+
|
|
831
|
+
masked_paths:
|
|
832
|
+
- /proc/kcore
|
|
833
|
+
- /proc/latency_stats
|
|
834
|
+
- /sys/firmware
|
|
835
|
+
|
|
836
|
+
# Optional: Mount volumes
|
|
837
|
+
volumes:
|
|
838
|
+
- name: workspace
|
|
839
|
+
path: /workspace
|
|
840
|
+
read_only: false
|
|
841
|
+
size: 1Gi
|
|
842
|
+
```
|
|
843
|
+
|
|
844
|
+
---
|
|
845
|
+
|
|
846
|
+
## Audit Logging
|
|
847
|
+
|
|
848
|
+
All security-relevant events MUST be logged in **OpenTelemetry-compatible format**.
|
|
849
|
+
|
|
850
|
+
### Required Events
|
|
851
|
+
|
|
852
|
+
| Event Type | Description | Required Attributes |
|
|
853
|
+
|------------|-------------|---------------------|
|
|
854
|
+
| `agent_started` | Agent initialization | `ossa.agent.id`, `ossa.instance.id`, `security.identity.urn` |
|
|
855
|
+
| `agent_stopped` | Agent shutdown | `ossa.agent.id`, `ossa.instance.id`, `shutdown_reason` |
|
|
856
|
+
| `capability_invoked` | Tool/capability execution | `ossa.capability.name`, `ossa.tool.name`, `security.user.id` |
|
|
857
|
+
| `secret_accessed` | Secret retrieved | `secret.provider`, `secret.path`, `access_result` |
|
|
858
|
+
| `secret_rotated` | Secret value changed | `secret.provider`, `secret.path`, `rotation_trigger` |
|
|
859
|
+
| `policy_violation` | Authorization denied | `policy.id`, `policy.effect`, `violation_reason` |
|
|
860
|
+
| `authentication_success` | Auth succeeded | `auth.method`, `auth.principal`, `auth.source_ip` |
|
|
861
|
+
| `authentication_failure` | Auth failed | `auth.method`, `auth.principal`, `auth.failure_reason` |
|
|
862
|
+
| `authorization_denied` | Authz failed | `authz.action`, `authz.resource`, `authz.principal` |
|
|
863
|
+
| `network_connection` | External network call | `network.peer.address`, `network.protocol`, `network.status` |
|
|
864
|
+
|
|
865
|
+
### Log Format
|
|
866
|
+
|
|
867
|
+
Audit logs MUST be structured as OpenTelemetry log records.
|
|
868
|
+
|
|
869
|
+
```json
|
|
870
|
+
{
|
|
871
|
+
"timestamp": "2025-12-04T15:30:00.000Z",
|
|
872
|
+
"severity_text": "INFO",
|
|
873
|
+
"severity_number": 9,
|
|
874
|
+
"body": "Capability invoked",
|
|
875
|
+
"attributes": {
|
|
876
|
+
"event.name": "capability_invoked",
|
|
877
|
+
"ossa.agent.id": "secure-agent",
|
|
878
|
+
"ossa.agent.version": "1.0.0",
|
|
879
|
+
"ossa.instance.id": "550e8400-e29b-41d4-a716-446655440000",
|
|
880
|
+
"ossa.session.id": "abc123",
|
|
881
|
+
"ossa.capability.name": "code_review",
|
|
882
|
+
"ossa.tool.name": "gitlab-api",
|
|
883
|
+
"security.user.id": "alice@acme.com",
|
|
884
|
+
"security.source_ip": "192.168.1.100"
|
|
885
|
+
},
|
|
886
|
+
"resource": {
|
|
887
|
+
"service.name": "ossa-runtime",
|
|
888
|
+
"service.version": "0.2.9",
|
|
889
|
+
"deployment.environment": "production"
|
|
890
|
+
},
|
|
891
|
+
"trace_id": "5b8aa5a2d2c872e8321cf37308d69df2",
|
|
892
|
+
"span_id": "051581bf3cb55c13"
|
|
893
|
+
}
|
|
894
|
+
```
|
|
895
|
+
|
|
896
|
+
### Retention and Immutability
|
|
897
|
+
|
|
898
|
+
Audit logs MUST be:
|
|
899
|
+
|
|
900
|
+
1. **Immutable**: Write-once, tamper-evident storage
|
|
901
|
+
2. **Retained**: Per compliance requirements (7 years for FedRAMP)
|
|
902
|
+
3. **Encrypted**: At-rest encryption with key rotation
|
|
903
|
+
4. **Searchable**: Indexed for compliance queries
|
|
904
|
+
|
|
905
|
+
```yaml
|
|
906
|
+
audit:
|
|
907
|
+
enabled: true
|
|
908
|
+
|
|
909
|
+
retention:
|
|
910
|
+
days: 2555 # 7 years
|
|
911
|
+
immutable: true
|
|
912
|
+
|
|
913
|
+
storage:
|
|
914
|
+
backend: elasticsearch
|
|
915
|
+
encryption:
|
|
916
|
+
enabled: true
|
|
917
|
+
algorithm: AES-256-GCM
|
|
918
|
+
key_ref: ${vault:secret/audit-encryption-key}
|
|
919
|
+
|
|
920
|
+
index_pattern: ossa-audit-logs-%{+YYYY.MM.dd}
|
|
921
|
+
|
|
922
|
+
events:
|
|
923
|
+
- agent_started
|
|
924
|
+
- agent_stopped
|
|
925
|
+
- capability_invoked
|
|
926
|
+
- secret_accessed
|
|
927
|
+
- secret_rotated
|
|
928
|
+
- policy_violation
|
|
929
|
+
- authentication_success
|
|
930
|
+
- authentication_failure
|
|
931
|
+
- authorization_denied
|
|
932
|
+
- network_connection
|
|
933
|
+
```
|
|
934
|
+
|
|
935
|
+
### OpenTelemetry Integration
|
|
936
|
+
|
|
937
|
+
Export audit logs using OpenTelemetry Log Exporter.
|
|
938
|
+
|
|
939
|
+
```typescript
|
|
940
|
+
import { LoggerProvider, BatchLogRecordProcessor } from '@opentelemetry/sdk-logs';
|
|
941
|
+
import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';
|
|
942
|
+
|
|
943
|
+
const logExporter = new OTLPLogExporter({
|
|
944
|
+
url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT + '/v1/logs',
|
|
945
|
+
headers: {
|
|
946
|
+
'Authorization': `Bearer ${process.env.OTEL_API_KEY}`
|
|
947
|
+
}
|
|
948
|
+
});
|
|
949
|
+
|
|
950
|
+
const loggerProvider = new LoggerProvider();
|
|
951
|
+
loggerProvider.addLogRecordProcessor(new BatchLogRecordProcessor(logExporter));
|
|
952
|
+
|
|
953
|
+
const logger = loggerProvider.getLogger('ossa-security-audit', '0.2.9');
|
|
954
|
+
|
|
955
|
+
// Emit audit event
|
|
956
|
+
logger.emit({
|
|
957
|
+
severityText: 'INFO',
|
|
958
|
+
body: 'Capability invoked',
|
|
959
|
+
attributes: {
|
|
960
|
+
'event.name': 'capability_invoked',
|
|
961
|
+
'ossa.agent.id': manifest.metadata.name,
|
|
962
|
+
'ossa.capability.name': capability.name,
|
|
963
|
+
'security.user.id': context.userId
|
|
964
|
+
}
|
|
965
|
+
});
|
|
966
|
+
```
|
|
967
|
+
|
|
968
|
+
---
|
|
969
|
+
|
|
970
|
+
## Security Best Practices
|
|
971
|
+
|
|
972
|
+
### 1. Principle of Least Privilege
|
|
973
|
+
|
|
974
|
+
Agents SHOULD request minimum permissions required.
|
|
975
|
+
|
|
976
|
+
```yaml
|
|
977
|
+
# ❌ Bad: Request all permissions
|
|
978
|
+
authorization:
|
|
979
|
+
rbac:
|
|
980
|
+
role: orchestrator # Too broad
|
|
981
|
+
|
|
982
|
+
# ✅ Good: Request specific permissions
|
|
983
|
+
authorization:
|
|
984
|
+
rbac:
|
|
985
|
+
role: worker
|
|
986
|
+
policies:
|
|
987
|
+
- allow: tools.execute
|
|
988
|
+
resources:
|
|
989
|
+
- gitlab-api
|
|
990
|
+
```
|
|
991
|
+
|
|
992
|
+
### 2. Defense in Depth
|
|
993
|
+
|
|
994
|
+
Layer multiple security controls.
|
|
995
|
+
|
|
996
|
+
```yaml
|
|
997
|
+
security:
|
|
998
|
+
# Layer 1: Identity
|
|
999
|
+
identity:
|
|
1000
|
+
urn: ossa:agent:acme:secure-agent:1.0.0
|
|
1001
|
+
attestation:
|
|
1002
|
+
type: x509
|
|
1003
|
+
|
|
1004
|
+
# Layer 2: Authentication
|
|
1005
|
+
authentication:
|
|
1006
|
+
methods:
|
|
1007
|
+
- mtls
|
|
1008
|
+
|
|
1009
|
+
# Layer 3: Authorization
|
|
1010
|
+
authorization:
|
|
1011
|
+
rbac:
|
|
1012
|
+
role: worker
|
|
1013
|
+
|
|
1014
|
+
# Layer 4: Sandboxing
|
|
1015
|
+
sandbox:
|
|
1016
|
+
isolation: container
|
|
1017
|
+
|
|
1018
|
+
# Layer 5: Network isolation
|
|
1019
|
+
network:
|
|
1020
|
+
egress:
|
|
1021
|
+
- https://api.gitlab.com
|
|
1022
|
+
|
|
1023
|
+
# Layer 6: Audit
|
|
1024
|
+
audit:
|
|
1025
|
+
enabled: true
|
|
1026
|
+
```
|
|
1027
|
+
|
|
1028
|
+
### 3. Secrets Hygiene
|
|
1029
|
+
|
|
1030
|
+
- **NEVER** commit secrets to version control
|
|
1031
|
+
- **ALWAYS** use secret references
|
|
1032
|
+
- **ROTATE** secrets regularly
|
|
1033
|
+
- **LIMIT** secret access to minimum required agents
|
|
1034
|
+
|
|
1035
|
+
### 4. Audit Everything
|
|
1036
|
+
|
|
1037
|
+
Enable comprehensive audit logging.
|
|
1038
|
+
|
|
1039
|
+
```yaml
|
|
1040
|
+
audit:
|
|
1041
|
+
enabled: true
|
|
1042
|
+
events:
|
|
1043
|
+
- agent_started
|
|
1044
|
+
- agent_stopped
|
|
1045
|
+
- capability_invoked
|
|
1046
|
+
- secret_accessed
|
|
1047
|
+
- policy_violation
|
|
1048
|
+
- authentication_success
|
|
1049
|
+
- authentication_failure
|
|
1050
|
+
- authorization_denied
|
|
1051
|
+
```
|
|
1052
|
+
|
|
1053
|
+
### 5. Compliance Mapping
|
|
1054
|
+
|
|
1055
|
+
Map security controls to compliance frameworks.
|
|
1056
|
+
|
|
1057
|
+
```yaml
|
|
1058
|
+
metadata:
|
|
1059
|
+
annotations:
|
|
1060
|
+
compliance.ossa.io/fedramp: AC-2,AC-3,AU-2,AU-9,SC-8,SC-13
|
|
1061
|
+
compliance.ossa.io/soc2: CC6.1,CC6.2,C1.1
|
|
1062
|
+
compliance.ossa.io/hipaa: 164.308(a)(3),164.312(a)(1),164.312(e)(1)
|
|
1063
|
+
```
|
|
1064
|
+
|
|
1065
|
+
See [compliance-profiles.md](./compliance-profiles.md) for full control mappings.
|
|
1066
|
+
|
|
1067
|
+
---
|
|
1068
|
+
|
|
1069
|
+
## TypeScript Types
|
|
1070
|
+
|
|
1071
|
+
```typescript
|
|
1072
|
+
export interface SecuritySpec {
|
|
1073
|
+
identity: AgentIdentity;
|
|
1074
|
+
authentication: AuthenticationConfig;
|
|
1075
|
+
authorization: AuthorizationConfig;
|
|
1076
|
+
secrets?: SecretsConfig;
|
|
1077
|
+
sandbox: SandboxConfig;
|
|
1078
|
+
audit: AuditConfig;
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
export interface AgentIdentity {
|
|
1082
|
+
urn: string; // ossa:agent:<org>:<name>:<version>
|
|
1083
|
+
attestation: AttestationConfig;
|
|
1084
|
+
labels?: Record<string, string>;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
export interface AttestationConfig {
|
|
1088
|
+
type: 'x509' | 'jwt' | 'spiffe';
|
|
1089
|
+
certificate_ref?: string;
|
|
1090
|
+
jwt_ref?: string;
|
|
1091
|
+
trust_anchor: string;
|
|
1092
|
+
validation?: ValidationRules;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
export interface AuthenticationConfig {
|
|
1096
|
+
methods: ('mtls' | 'jwt' | 'oidc' | 'api_key')[];
|
|
1097
|
+
mtls?: MutualTLSConfig;
|
|
1098
|
+
jwt?: JWTConfig;
|
|
1099
|
+
oidc?: OIDCConfig;
|
|
1100
|
+
api_key?: APIKeyConfig;
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
export interface AuthorizationConfig {
|
|
1104
|
+
rbac: RBACConfig;
|
|
1105
|
+
abac?: ABACConfig;
|
|
1106
|
+
policies?: Policy[];
|
|
1107
|
+
default_policy?: 'allow' | 'deny';
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
export interface RBACConfig {
|
|
1111
|
+
role: 'orchestrator' | 'worker' | 'auditor';
|
|
1112
|
+
permissions?: string[];
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
export interface SandboxConfig {
|
|
1116
|
+
isolation: 'process' | 'container' | 'vm';
|
|
1117
|
+
resources: ResourceLimits;
|
|
1118
|
+
network?: NetworkPolicy;
|
|
1119
|
+
filesystem?: FilesystemPolicy;
|
|
1120
|
+
syscall_filter?: SyscallFilter;
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
export interface ResourceLimits {
|
|
1124
|
+
memory: string; // e.g., "512Mi"
|
|
1125
|
+
cpu: string; // e.g., "1000m"
|
|
1126
|
+
ephemeral_storage?: string;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
export interface AuditConfig {
|
|
1130
|
+
enabled: boolean;
|
|
1131
|
+
events: AuditEvent[];
|
|
1132
|
+
retention?: RetentionPolicy;
|
|
1133
|
+
storage?: StorageConfig;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
export type AuditEvent =
|
|
1137
|
+
| 'agent_started'
|
|
1138
|
+
| 'agent_stopped'
|
|
1139
|
+
| 'capability_invoked'
|
|
1140
|
+
| 'secret_accessed'
|
|
1141
|
+
| 'secret_rotated'
|
|
1142
|
+
| 'policy_violation'
|
|
1143
|
+
| 'authentication_success'
|
|
1144
|
+
| 'authentication_failure'
|
|
1145
|
+
| 'authorization_denied'
|
|
1146
|
+
| 'network_connection';
|
|
1147
|
+
```
|
|
1148
|
+
|
|
1149
|
+
---
|
|
1150
|
+
|
|
1151
|
+
## Validation CLI
|
|
1152
|
+
|
|
1153
|
+
```bash
|
|
1154
|
+
# Validate security configuration
|
|
1155
|
+
ossa validate --security manifest.yaml
|
|
1156
|
+
|
|
1157
|
+
# Check RBAC permissions
|
|
1158
|
+
ossa rbac check --agent secure-agent --action tools.execute
|
|
1159
|
+
|
|
1160
|
+
# Test secret references
|
|
1161
|
+
ossa secrets test manifest.yaml
|
|
1162
|
+
|
|
1163
|
+
# Audit log query
|
|
1164
|
+
ossa audit query --event capability_invoked --since 1h
|
|
1165
|
+
|
|
1166
|
+
# Generate security report
|
|
1167
|
+
ossa security-report --format pdf manifest.yaml
|
|
1168
|
+
```
|
|
1169
|
+
|
|
1170
|
+
---
|
|
1171
|
+
|
|
1172
|
+
## Runtime Enforcement Pseudo-Code
|
|
1173
|
+
|
|
1174
|
+
```typescript
|
|
1175
|
+
class OSSASecurityRuntime {
|
|
1176
|
+
async validateAgent(manifest: AgentManifest): Promise<void> {
|
|
1177
|
+
// 1. Validate identity
|
|
1178
|
+
await this.validateIdentity(manifest.spec.security.identity);
|
|
1179
|
+
|
|
1180
|
+
// 2. Authenticate agent
|
|
1181
|
+
await this.authenticate(manifest.spec.security.authentication);
|
|
1182
|
+
|
|
1183
|
+
// 3. Authorize startup
|
|
1184
|
+
await this.authorize(manifest.spec.security.authorization);
|
|
1185
|
+
|
|
1186
|
+
// 4. Initialize sandbox
|
|
1187
|
+
await this.initializeSandbox(manifest.spec.security.sandbox);
|
|
1188
|
+
|
|
1189
|
+
// 5. Setup audit logging
|
|
1190
|
+
await this.initializeAudit(manifest.spec.security.audit);
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
async executeCapability(
|
|
1194
|
+
capability: string,
|
|
1195
|
+
context: SecurityContext
|
|
1196
|
+
): Promise<void> {
|
|
1197
|
+
// 1. Check authorization
|
|
1198
|
+
if (!this.isAuthorized(capability, context)) {
|
|
1199
|
+
await this.auditEvent('authorization_denied', { capability, context });
|
|
1200
|
+
throw new AuthorizationError('Access denied');
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
// 2. Audit invocation
|
|
1204
|
+
await this.auditEvent('capability_invoked', { capability, context });
|
|
1205
|
+
|
|
1206
|
+
// 3. Execute with sandbox
|
|
1207
|
+
try {
|
|
1208
|
+
await this.sandbox.execute(capability, context);
|
|
1209
|
+
} catch (error) {
|
|
1210
|
+
await this.auditEvent('capability_failed', { capability, error });
|
|
1211
|
+
throw error;
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
async accessSecret(secretRef: string): Promise<string> {
|
|
1216
|
+
// 1. Parse secret reference
|
|
1217
|
+
const { provider, path } = this.parseSecretRef(secretRef);
|
|
1218
|
+
|
|
1219
|
+
// 2. Check authorization
|
|
1220
|
+
if (!this.isAuthorized(`secrets.read.${provider}`, context)) {
|
|
1221
|
+
await this.auditEvent('secret_access_denied', { secretRef });
|
|
1222
|
+
throw new AuthorizationError('Secret access denied');
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
// 3. Retrieve secret
|
|
1226
|
+
const value = await this.secretsProvider.get(provider, path);
|
|
1227
|
+
|
|
1228
|
+
// 4. Audit access
|
|
1229
|
+
await this.auditEvent('secret_accessed', { provider, path });
|
|
1230
|
+
|
|
1231
|
+
return value;
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
```
|
|
1235
|
+
|
|
1236
|
+
---
|
|
1237
|
+
|
|
1238
|
+
## References
|
|
1239
|
+
|
|
1240
|
+
- [SPIFFE/SPIRE](https://spiffe.io/) - Zero-trust identity framework
|
|
1241
|
+
- [NIST SP 800-204](https://csrc.nist.gov/publications/detail/sp/800-204/final) - Security Strategies for Microservices
|
|
1242
|
+
- [OpenTelemetry Logs](https://opentelemetry.io/docs/specs/otel/logs/) - Log data model
|
|
1243
|
+
- [seccomp-bpf](https://www.kernel.org/doc/html/latest/userspace-api/seccomp_filter.html) - Syscall filtering
|
|
1244
|
+
- [OSSA Compliance Profiles](./compliance-profiles.md) - Framework mappings
|
|
1245
|
+
- [OSSA Semantic Conventions](./semantic-conventions.md) - Observability attributes
|