diff --git a/.chloggen/audit-logging-semconv.yaml b/.chloggen/audit-logging-semconv.yaml new file mode 100644 index 0000000000..a3f56e50b9 --- /dev/null +++ b/.chloggen/audit-logging-semconv.yaml @@ -0,0 +1,17 @@ +change_type: new_component + +component: audit + +note: Add `audit.*` semantic conventions for the OpenTelemetry Audit Logging signal (OTEP 0267). + +issues: [7] + +subtext: | + Introduces a dedicated `audit.*` attribute namespace for compliance-critical, + lossless audit logging (ISO 27001, SOC 2, PCI-DSS, HIPAA). Attribute groups + cover actor identity (`audit.actor.*`), action and outcome (`audit.action`, + `audit.outcome`), target resource (`audit.target.*`), network source + (`audit.source.*`), per-record cryptographic integrity (`audit.integrity.value`), + and resource-level signing metadata (`audit.integrity.algorithm`, + `audit.integrity.certificate`). An append-only hash-chain is supported via + `audit.sequence.number` and `audit.prev.hash`. diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 05a426e838..f0356caf7b 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -26,6 +26,7 @@ body: - area:app - area:artifact - area:aspnetcore + - area:audit - area:aws - area:azure - area:browser diff --git a/.github/ISSUE_TEMPLATE/change_proposal.yaml b/.github/ISSUE_TEMPLATE/change_proposal.yaml index c5d1ab80da..6587d15731 100644 --- a/.github/ISSUE_TEMPLATE/change_proposal.yaml +++ b/.github/ISSUE_TEMPLATE/change_proposal.yaml @@ -18,6 +18,7 @@ body: - area:app - area:artifact - area:aspnetcore + - area:audit - area:aws - area:azure - area:browser diff --git a/.github/ISSUE_TEMPLATE/new-conventions.yaml b/.github/ISSUE_TEMPLATE/new-conventions.yaml index 1f84a59d5b..77669e30df 100644 --- a/.github/ISSUE_TEMPLATE/new-conventions.yaml +++ b/.github/ISSUE_TEMPLATE/new-conventions.yaml @@ -29,6 +29,7 @@ body: - area:app - area:artifact - area:aspnetcore + - area:audit - area:aws - area:azure - area:browser diff --git a/docs/audit/audit-logs.md b/docs/audit/audit-logs.md new file mode 100644 index 0000000000..0a43c260f4 --- /dev/null +++ b/docs/audit/audit-logs.md @@ -0,0 +1,419 @@ + + +# Semantic Conventions for Audit Logs + +**Status**: [Development][DocumentStatus] + + + +- [Motivation and compliance context](#motivation-and-compliance-context) +- [Signal separation](#signal-separation) +- [Recording an audit record](#recording-an-audit-record) + - [LogRecord fields](#logrecord-fields) + - [Event name](#event-name) + - [Attributes](#attributes) +- [Mandatory attributes](#mandatory-attributes) +- [Optional attribute groups](#optional-attribute-groups) + - [Actor enrichment](#actor-enrichment) + - [Target](#target) + - [Source / origin](#source--origin) + - [Integrity and tamper-evidence](#integrity-and-tamper-evidence) + - [Hash-chain ordering](#hash-chain-ordering) +- [AuditReceipt](#auditreceipt) +- [SDK invariants](#sdk-invariants) +- [Compliance mapping](#compliance-mapping) +- [Examples](#examples) + - [Successful login](#successful-login) + - [Privileged resource deletion with integrity](#privileged-resource-deletion-with-integrity) + + + +## Motivation and compliance context + +Compliance frameworks (ISO 27001, SOC 2, PCI-DSS, HIPAA) mandate that +security-relevant events are captured completely, without loss, and in a +tamper-evident form. The standard OpenTelemetry Log signal is intentionally +designed to allow sampling, back-pressure shedding, and record transformation +— behaviours that are incompatible with these requirements. + +The **Audit Logging signal** is a dedicated pipeline that provides: + +- Guaranteed delivery with at-least-once semantics. +- An `AuditReceipt` returned synchronously once the sink acknowledges. +- Cryptographic integrity via per-record HMAC or digital signature. +- An append-only hash chain that makes retroactive tampering detectable. +- Complete separation from the observability log pipeline. + +> [!IMPORTANT] +> +> **Do NOT** use the standard `Logger` API to emit audit records. +> Audit records MUST be emitted through the dedicated `AuditLogger` obtained +> from an `AuditProvider`. Routing audit records through the observability +> pipeline risks silent loss, sampling, and transformation — any of which +> constitutes a compliance violation. + +## Signal separation + +The audit signal uses the same `LogRecord` data structure as the standard log +signal, but travels through a completely separate pipeline: + +``` +Application code + │ + ▼ +AuditLogger.emit(AuditRecord) ──► returns AuditReceipt + │ (via AuditProvider) + ▼ +AuditRecordProcessor chain (additive-only; enrichment, no filtering) + │ + ▼ +AuditRecordExporter ──POST /v1/audit──► Audit sink / Tier-2 Collector +``` + +The exporter uses the OTLP `ExportLogsServiceRequest` payload with an +`audit: true` flag on the `ResourceLogs` message to distinguish audit traffic +from `/v1/logs` observability traffic. + +## Recording an audit record + +### LogRecord fields + +The following `LogRecord` fields are used by audit records: + +| Field | Requirement | Notes | +| --- | --- | --- | +| `Timestamp` | MUST | Time the auditable action occurred (nanoseconds UTC). | +| `ObservedTimestamp` | MUST | SDK sets this to the current wall-clock time if omitted by the caller. | +| `EventName` | MUST | Dot-separated, lowercase name (see [Event name](#event-name)). | +| `Body` | MAY | Free-form human-readable summary. MUST NOT duplicate mandatory attributes. | +| `SeverityNumber` | SHOULD NOT | Not meaningful for audit records. | +| `InstrumentationScope` | MUST NOT | Leave empty (or SDK name/version as a technical marker only). | + +### Event name + +The event name MUST follow the +[naming conventions](/docs/general/naming.md) and use the prefix `audit.` +followed by a dot-separated, lowercase description of the auditable action. + +Examples: `audit.user.login`, `audit.config.change`, `audit.resource.delete`, +`audit.permission.grant`. + +### Attributes + +All audit-specific data is carried as `LogRecord` attributes in the `audit.*` +namespace. + + + + + + +**Attributes:** + +| Key | Stability | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | --- | +| [`audit.action`](/docs/registry/attributes/audit.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The auditable operation that was performed. [1] | `LOGIN`; `DELETE`; `GRANT`; `CONFIG_CHANGE` | +| [`audit.actor.id`](/docs/registry/attributes/audit.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The stable identifier of the actor that performed the action. [2] | `u8472`; `svc-deployer`; `arn:aws:iam::123456789012:role/DeployRole` | +| [`audit.actor.type`](/docs/registry/attributes/audit.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The category of principal that performed the action. | `user`; `service`; `system` | +| [`audit.outcome`](/docs/registry/attributes/audit.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | The result of the auditable action. | `success`; `failure`; `unknown` | +| [`audit.record.id`](/docs/registry/attributes/audit.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Required` | string | A stable, globally unique identifier for this audit record. [3] | `3fa85f64-5717-4562-b3fc-2c963f66afa6` | +| [`audit.integrity.algorithm`](/docs/registry/attributes/audit.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` [4] | string | The algorithm used to compute `audit.integrity.value`. [5] | `ES256`; `EdDSA`; `HMAC-SHA256` | +| [`audit.integrity.canonicalization`](/docs/registry/attributes/audit.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` [6] | string | The canonicalization scheme applied to the record before signing or MACing. [7] | `jcs` | +| [`audit.integrity.certificate`](/docs/registry/attributes/audit.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` [8] | string | A reference to the key or certificate used for `audit.integrity.value`. [9] | `key-2024-01`; `SHA256:ab12cd34...` | +| [`audit.sequence.end`](/docs/registry/attributes/audit.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Conditionally Required` [10] | boolean | Set to `true` on the last record of a gracefully closed audit stream. [11] | `true` | +| [`audit.integrity.signer`](/docs/registry/attributes/audit.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` When `audit.integrity.value` is present. | string | Identifies which tier produced the integrity proof in `audit.integrity.value`. [12] | `producer`; `collector` | +| [`audit.sequence.number`](/docs/registry/attributes/audit.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` [13] | int | A monotonically increasing counter assigned to each record within a single audit stream. [14] | `1`; `42`; `1000001` | +| [`audit.sequence.previous_hash`](/docs/registry/attributes/audit.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` [15] | string | SHA-256 hex digest of the `IntegrityHash` field of the immediately preceding record in the same audit stream. [16] | `a3f1c2e4b5d6a7f8e9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2` | +| [`audit.sequence.previous_record_id`](/docs/registry/attributes/audit.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` [17] | string | The `audit.record.id` of the immediately preceding record in the same audit stream. [18] | `3fa85f64-5717-4562-b3fc-2c963f66afa6` | +| [`audit.sequence.stream_id`](/docs/registry/attributes/audit.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` [19] | string | An opaque identifier that scopes the hash chain to a single audit stream. [20] | `7c9e6679-7425-40de-944b-e07fc1f90ae7` | +| [`audit.source.id`](/docs/registry/attributes/audit.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` [21] | string | A stable identifier for the source system or network endpoint that originated the action. [22] | `192.0.2.42`; `device-uuid-abcd1234` | +| [`audit.source.type`](/docs/registry/attributes/audit.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` When `audit.source.id` is present. | string | The category of origin that initiated the action. [23] | `IPv4`; `IPv6`; `hostname` | +| [`audit.target.id`](/docs/registry/attributes/audit.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` [24] | string | The stable identifier of the resource that was the target of the action. [25] | `document-42`; `arn:aws:s3:::my-bucket/key`; `/api/v1/users/9f81` | +| [`audit.target.type`](/docs/registry/attributes/audit.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Recommended` [26] | string | The type or resource kind of the target. [27] | `User`; `apps/v1/Deployment`; `finance.invoice` | +| [`audit.actor.name`](/docs/registry/attributes/audit.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | A human-readable display name or username of the actor. [28] | `alice`; `alice@example.com`; `Alice Smith` | +| [`audit.integrity.value`](/docs/registry/attributes/audit.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | base64-encoded cryptographic signature or MAC covering this record. [29] | `SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c` | +| [`audit.schema.version`](/docs/registry/attributes/audit.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The version of the audit semantic conventions schema used by this record. [30] | `1.0.0`; `1.2.0` | +| [`audit.target.name`](/docs/registry/attributes/audit.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | A human-readable name or label of the target resource. [31] | `production-deploy`; `Q1-invoice-7734` | + +**[1] `audit.action`:** SHOULD be an uppercase verb from a controlled vocabulary where possible. Well-known values include `LOGIN`, `LOGOUT`, `READ`, `CREATE`, `UPDATE`, `DELETE`, `GRANT`, `REVOKE`, `EXPORT`, `IMPORT`, `EXECUTE`, `APPROVE`, `REJECT`. Custom values MAY be used when none of the well-known values applies, but SHOULD be documented in the producing service's changelog. + +**[2] `audit.actor.id`:** This SHOULD be an opaque, durable identifier (e.g. a user-UUID, service account name, or IAM principal ARN) that remains stable across sessions. Avoid using mutable display names or email addresses as the primary ID. + +**[3] `audit.record.id`:** The SDK MUST auto-generate a UUID v4 when the caller omits this field. The value MUST remain identical across all retries of the same record. Records with the same `audit.record.id` and identical payload hash are treated as idempotent duplicates by compliant sinks. + +**[4] `audit.integrity.algorithm`:** Required (as a Resource attribute) when `audit.integrity.value` is present on any record emitted from this resource. + +**[5] `audit.integrity.algorithm`:** MUST be set as a Resource attribute whenever `audit.integrity.value` is present on any record emitted by this resource. Use a JWA identifier (RFC 7518) for asymmetric signatures (e.g. `ES256`, `RS256`, `EdDSA`) or an IANA MAC Algorithm identifier for symmetric MACs (e.g. `HMAC-SHA256`). + +**[6] `audit.integrity.canonicalization`:** Required when a canonicalization scheme other than `jcs` (RFC 8785) is used. Omit when `jcs` is used. + +**[7] `audit.integrity.canonicalization`:** Defaults to `jcs` (RFC 8785 JSON Canonicalization Scheme). Set explicitly when any other canonicalization is used so that verifiers can reproduce the exact byte sequence that was signed. MUST be omitted when `jcs` is used. + +**[8] `audit.integrity.certificate`:** Required (as a Resource attribute, together with `audit.integrity.algorithm`) when `audit.integrity.value` is present on any record emitted from this resource. + +**[9] `audit.integrity.certificate`:** MUST be set as a Resource attribute (together with `audit.integrity.algorithm`) whenever `audit.integrity.value` is present on any record emitted by this resource. Because a service instance uses a single signing key for its entire lifetime, both `audit.integrity.algorithm` and `audit.integrity.certificate` are constant across all records from the same resource and therefore belong on the Resource, not on individual records. +Acceptable forms (in order of preference): a Key ID / `kid` (JOSE header parameter), a DER-encoded X.509 certificate base64-encoded, an X.509 certificate fingerprint (SHA-256 hex), or an Issuer + Serial Number pair. Receivers MUST NOT use this field alone for trust decisions; key validation MUST be performed out-of-band. + +**[10] `audit.sequence.end`:** Required on the final record of a gracefully closed stream (emitted during `ForceFlush` or `Shutdown`). Omit on all other records. + +**[11] `audit.sequence.end`:** Emitted by the SDK during `ForceFlush` or `Shutdown` to mark the terminal record of a stream. Absence is normal for all intermediate records — do not treat absence as an error. + +**[12] `audit.integrity.signer`:** `producer` means the SDK computed the value before export. `collector` means a Tier-2 Collector signed or re-signed the record after receiving it. When absent, `producer` SHOULD be assumed. + +**[13] `audit.sequence.number`:** When the emitting service maintains a monotonic per-stream counter. Required when `audit.sequence.previous_hash` is present. + +**[14] `audit.sequence.number`:** Compliant sinks and Tier-2 Collectors MUST verify that consecutive records within the same stream have strictly increasing sequence numbers. Gaps MUST trigger a gap-detection warning and a security event log entry. The counter SHOULD start at 1 and increment by 1 per emitted record, but implementations MAY use a larger step if records can be emitted concurrently from multiple threads. + +**[15] `audit.sequence.previous_hash`:** When the emitting service implements a hash chain for tamper detection. Requires `audit.sequence.number` to be present. MUST be omitted on the genesis record of a stream. + +**[16] `audit.sequence.previous_hash`:** Including the previous record's sink-acknowledged hash in the current record creates an append-only hash chain. Any retroactive modification of a record is detectable by re-verifying the chain. MUST be omitted on the genesis (first) record of a stream — absence is the normative signal; no magic sentinel constant is required. + +**[17] `audit.sequence.previous_record_id`:** When `audit.sequence.previous_hash` is present. Provides a resolvable locator for the predecessor record across storage boundaries. MUST be omitted on the genesis record of a stream. + +**[18] `audit.sequence.previous_record_id`:** Provides a resolvable locator for the predecessor across shards, storage systems, and retention boundaries, complementing `audit.sequence.previous_hash`. MUST be omitted on the genesis (first) record of a stream. + +**[19] `audit.sequence.stream_id`:** When the emitting service uses hash-chain ordering. Scopes the chain to a single `AuditLogger` instance. + +**[20] `audit.sequence.stream_id`:** The SDK SHOULD generate a UUID v4 once per `AuditLogger` instance and include it on every record emitted by that logger. + +**[21] `audit.source.id`:** When the network origin or calling device of the action is known and meaningful for compliance review (e.g. PCI-DSS access logging). + +**[22] `audit.source.id`:** MAY be an IP address, a device UUID, or a service mesh endpoint ID. Prefer stable identifiers (device UUID) over ephemeral ones (IP address) when both are available. + +**[23] `audit.source.type`:** Well-known values: `ipv4`, `ipv6`, `hostname`. Custom values MAY be used. + +**[24] `audit.target.id`:** When the action is performed on a specific, identifiable resource. + +**[25] `audit.target.id`:** SHOULD be the primary key, ARN, URI, or equivalent durable identifier of the resource. When acting on a collection rather than a single resource, this MAY contain the collection identifier. + +**[26] `audit.target.type`:** When `audit.target.id` is present and the resource kind adds meaningful context for audit review. + +**[27] `audit.target.type`:** SHOULD use a fully-qualified type where available (e.g. a Kubernetes GroupVersionResource or an IAM resource type). For domain-specific types a short qualified name MAY be used (e.g. `finance.invoice`). + +**[28] `audit.actor.name`:** MAY be a login name, email address, or display name. This value is informational only; use `audit.actor.id` for stable identity correlation. + +**[29] `audit.integrity.value`:** The input to the signing / MAC operation MUST be the canonical serialization of the `AuditRecord` with all `audit.integrity.*` attributes excluded. The default canonicalization is JCS (RFC 8785); set `audit.integrity.canonicalization` when a different scheme is used. `audit.integrity.algorithm` MUST be set on the emitting Resource whenever this attribute is present. + +**[30] `audit.schema.version`:** Follows semantic versioning (MAJOR.MINOR.PATCH). Receivers MAY use this value to select the matching validation schema for the record's attribute vocabulary. + +**[31] `audit.target.name`:** Informational only. Use `audit.target.id` for stable identity correlation. + +--- + +`audit.actor.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `service` | An automated service or daemon acting on its own behalf. | ![Development](https://img.shields.io/badge/-development-blue) | +| `system` | An internal system component (scheduler, GC, background job) that is not externally authenticated. | ![Development](https://img.shields.io/badge/-development-blue) | +| `user` | A human user authenticated to the system. | ![Development](https://img.shields.io/badge/-development-blue) | + +--- + +`audit.outcome` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `failure` | The action was attempted but did not complete successfully (e.g. authorization denied, validation error, resource not found). | ![Development](https://img.shields.io/badge/-development-blue) | +| `success` | The action completed successfully and produced its intended effect. | ![Development](https://img.shields.io/badge/-development-blue) | +| `unknown` | The outcome could not be determined at the time the event was emitted, for example because the operation is async. | ![Development](https://img.shields.io/badge/-development-blue) | + + + + + +--- + +`audit.actor.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `service` | An automated service or daemon acting on its own behalf. | ![Development](https://img.shields.io/badge/-development-blue) | +| `system` | An internal system component that is not externally authenticated. | ![Development](https://img.shields.io/badge/-development-blue) | +| `user` | A human user authenticated to the system. | ![Development](https://img.shields.io/badge/-development-blue) | + +--- + +`audit.outcome` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `failure` | The action was attempted but did not complete successfully. | ![Development](https://img.shields.io/badge/-development-blue) | +| `success` | The action completed successfully and produced its intended effect. | ![Development](https://img.shields.io/badge/-development-blue) | +| `unknown` | The outcome could not be determined at emit time (e.g. async operation). | ![Development](https://img.shields.io/badge/-development-blue) | + +## Mandatory attributes + +The five mandatory attributes MUST be present on every audit record. +Missing any of them is a hard error; the SDK MUST NOT silently drop the record. + +| Attribute | Auto-generated? | +| --- | --- | +| `audit.record.id` | Yes — SDK generates UUID v4 if absent. | +| `audit.actor.id` | No — caller MUST provide. | +| `audit.actor.type` | No — caller MUST provide. | +| `audit.action` | No — caller MUST provide. | +| `audit.outcome` | No — caller MUST provide. | + +## Optional attribute groups + +### Actor enrichment + +`audit.actor.name` provides a human-readable label for the actor, useful for +display in audit review UIs. It MUST NOT be used as the primary identifier; +use `audit.actor.id` for stable correlation. + +### Target + +`audit.target.id` and `audit.target.type` describe the resource acted upon. +Include them whenever the action has a specific, identifiable resource object. +For bulk operations, `audit.target.id` MAY hold a collection identifier. + +### Source / origin + +`audit.source.id` and `audit.source.type` record the network endpoint or +device from which the action originated. PCI-DSS Requirement 10 mandates +capturing the originating IP for all access to cardholder data environments; +use these attributes to satisfy that requirement. + +### Integrity and tamper-evidence + +The integrity triplet (`audit.integrity.value`, `audit.integrity.algorithm`, +`audit.integrity.certificate`) enables the audit sink to verify that a record +was not altered in transit. + +- `audit.integrity.algorithm` and `audit.integrity.certificate` are both + **Resource attributes**. A service instance uses a single signing key for + its entire lifetime, so both values are constant across all records emitted + by the same resource. They MUST NOT be placed on individual records. +- `audit.integrity.value` is the per-record base64-encoded signature/MAC + over the canonical serialization of the record (with `audit.integrity.value` + itself absent from the input). +- On acknowledgement, the sink returns an `AuditReceipt` containing an + `IntegrityHash` (SHA-256 of the persisted record). The emitter SHOULD + compute the same hash locally and compare; a mismatch MUST be treated as + a hard error. + +Compliant implementations SHOULD use asymmetric signing (`ES256` or `EdDSA`) +to allow the sink to verify without possessing the signing key. + +### Hash-chain ordering + +`audit.sequence.number` and `audit.sequence.previous_hash` together form an append-only +hash chain: + +``` +Record N: sequence.number=N, sequence.previous_hash=IntegrityHash(Record N-1) +Record N+1: sequence.number=N+1, sequence.previous_hash=IntegrityHash(Record N) +``` + +Any retroactive modification of Record N causes the `audit.sequence.previous_hash` of Record N+1 +to become invalid, making the tampering detectable without re-signing. +The genesis (first) record of a stream MUST omit `audit.sequence.previous_hash` — absence is the normative signal. + +## AuditReceipt + +`emit()` blocks until the audit sink acknowledges the record, then returns +an `AuditReceipt`: + +| Field | Type | Description | +| --- | --- | --- | +| `RecordId` | string | Echoes the caller's `audit.record.id`. | +| `IntegrityHash` | string | SHA-256 hex digest of the record as persisted by the sink. | +| `SinkTimestamp` | uint64 | Nanoseconds since UNIX epoch when the sink wrote the record. | + +The emitter SHOULD compute `SHA-256(canonical_record)` locally and compare to +`IntegrityHash`. A mismatch means the record was altered after transmission +and MUST be treated as a hard error. + +`emit()` MUST raise a hard error if the sink does not respond within the +configured timeout. It MUST NOT return successfully without a valid receipt. + +## SDK invariants + +The following invariants MUST be respected by every SDK implementation: + +| # | Invariant | +|----|------------------------------------------------------------------------------------------------------------------------------------------------| +| 1 | `AuditProvider` MUST NOT accept sampler or sampling-rate configuration. | +| 2 | `emit()` MUST block until the sink acknowledges, or raise a hard error. | +| 3 | `emit()` MUST return a valid `AuditReceipt`. | +| 4 | The queue MUST be disk-backed or unbounded in-memory — **never** silently drops. | +| 5 | Processors are additive-only: MAY enrich; MUST NOT delete, filter, or transform. | +| 6 | Transport: `POST /v1/audit` with `audit: true` on `ResourceLogs`. | +| 7 | `partial_success` in a sink response MUST be treated as a hard `Failure`. | +| 8 | `InstrumentationScope` MUST be left empty. | +| 9 | SDK exposes metrics: `audit.records.emitted`, `audit.records.exported`, `audit.records.dropped`, `audit.queue.depth`, `audit.export.duration`. | +| 10 | Non-zero `audit.records.dropped` MUST trigger an operational alert. | +| 11 | `emit()` called on a mandatory attribute missing or empty MUST raise a hard error — MUST NOT silently drop the record. | +| 12 | `emit()` called after `Shutdown` MUST raise a hard error — MUST NOT silently drop the record. | + +## Compliance mapping + +| Requirement | Audit signal feature | +|-------------------------------------|---------------------------------------------------------------------------| +| ISO 27001 A.8.15 — Logging | All actions recorded; logs protected via integrity attributes. | +| ISO 27001 A.8.17 — Clock sync | SDK warns when `\|Timestamp − ObservedTimestamp\|` > 5 s. | +| SOC 2 CC7.2 — Anomaly detection | `audit.records.dropped` metric + operational alert. | +| PCI-DSS Req. 10.2 — Audit trails | Mandatory attributes cover all required fields; `audit.source.id` for IP. | +| PCI-DSS Req. 10.5 — Log protection | `audit.integrity.value` + `audit.sequence.previous_hash` hash chain. | +| HIPAA § 164.312(b) — Audit controls | `audit.actor.*` + `audit.target.*` cover ePHI access logging. | + +## Examples + +### Successful login + +```json +{ + "Timestamp": "2026-06-03T10:00:00.000000000Z", + "ObservedTimestamp": "2026-06-03T10:00:00.000100000Z", + "EventName": "audit.user.login", + "Attributes": { + "audit.record.id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "audit.actor.id": "u8472", + "audit.actor.type": "user", + "audit.actor.name": "alice", + "audit.action": "LOGIN", + "audit.outcome": "success", + "audit.source.id": "192.0.2.42", + "audit.source.type": "ipv4" + } +} +``` + +### Privileged resource deletion with integrity + +```json +{ + "Timestamp": "2026-06-03T10:05:00.000000000Z", + "ObservedTimestamp": "2026-06-03T10:05:00.000050000Z", + "EventName": "audit.resource.delete", + "Resource": { + "service.name": "billing-service", + "service.version": "3.1.0", + "audit.integrity.algorithm": "ES256", + "audit.integrity.certificate": "key-2024-01" + }, + "Attributes": { + "audit.record.id": "7c9e6679-7425-40de-944b-e07fc1f90ae7", + "audit.actor.id": "svc-deployer", + "audit.actor.type": "service", + "audit.action": "DELETE", + "audit.outcome": "success", + "audit.target.id": "invoice-8819", + "audit.target.type": "finance.invoice", + "audit.source.id": "192.0.2.99", + "audit.source.type": "ipv4", + "audit.sequence.stream_id": "b1d2e3f4-a5b6-7c8d-9e0f-a1b2c3d4e5f6", + "audit.sequence.number": 42, + "audit.sequence.previous_hash": "a3f1c2e4b5d6a7f8e9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2", + "audit.integrity.value": "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV+adQssw5c", + "audit.integrity.signer": "producer", + "audit.schema.version": "1.0.0" + } +} +``` + +[DocumentStatus]: https://opentelemetry.io/docs/specs/otel/document-status/ diff --git a/docs/registry/attributes/README.md b/docs/registry/attributes/README.md index 64c71e2869..80c0928acd 100644 --- a/docs/registry/attributes/README.md +++ b/docs/registry/attributes/README.md @@ -35,6 +35,7 @@ Currently, the following namespaces exist: - [App](app.md) - [Artifact](artifact.md) - [Aspnetcore](aspnetcore.md) +- [Audit](audit.md) - [AWS](aws.md) - [Azure](azure.md) - [Browser](browser.md) diff --git a/docs/registry/attributes/audit.md b/docs/registry/attributes/audit.md new file mode 100644 index 0000000000..0faa6237d0 --- /dev/null +++ b/docs/registry/attributes/audit.md @@ -0,0 +1,158 @@ + + + +# Audit + +- [Audit Action Attributes](#audit-action-attributes) +- [Audit Actor Attributes](#audit-actor-attributes) +- [Audit Integrity Attributes](#audit-integrity-attributes) +- [Audit Record Attributes](#audit-record-attributes) +- [Audit Sequence Attributes](#audit-sequence-attributes) +- [Audit Source Attributes](#audit-source-attributes) +- [Audit Target Attributes](#audit-target-attributes) + +## Audit Action Attributes + +Attributes that describe the operation performed by the actor. + +**Attributes:** + +| Key | Stability | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | +| `audit.action` | ![Development](https://img.shields.io/badge/-development-blue) | string | The auditable operation that was performed. [1] | `LOGIN`; `DELETE`; `GRANT`; `CONFIG_CHANGE` | +| `audit.outcome` | ![Development](https://img.shields.io/badge/-development-blue) | string | The result of the auditable action. | `success`; `failure`; `unknown` | + +**[1] `audit.action`:** SHOULD be an uppercase verb from a controlled vocabulary where possible. Well-known values include `LOGIN`, `LOGOUT`, `READ`, `CREATE`, `UPDATE`, `DELETE`, `GRANT`, `REVOKE`, `EXPORT`, `IMPORT`, `EXECUTE`, `APPROVE`, `REJECT`. Custom values MAY be used when none of the well-known values applies, but SHOULD be documented in the producing service's changelog. + +--- + +`audit.outcome` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `failure` | The action was attempted but did not complete successfully (e.g. authorization denied, validation error, resource not found). | ![Development](https://img.shields.io/badge/-development-blue) | +| `success` | The action completed successfully and produced its intended effect. | ![Development](https://img.shields.io/badge/-development-blue) | +| `unknown` | The outcome could not be determined at the time the event was emitted, for example because the operation is async. | ![Development](https://img.shields.io/badge/-development-blue) | + +## Audit Actor Attributes + +Attributes that identify the principal (human user, service, or automated system component) that initiated the auditable action. + +**Attributes:** + +| Key | Stability | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | +| `audit.actor.id` | ![Development](https://img.shields.io/badge/-development-blue) | string | The stable identifier of the actor that performed the action. [2] | `u8472`; `svc-deployer`; `arn:aws:iam::123456789012:role/DeployRole` | +| `audit.actor.name` | ![Development](https://img.shields.io/badge/-development-blue) | string | A human-readable display name or username of the actor. [3] | `alice`; `alice@example.com`; `Alice Smith` | +| `audit.actor.type` | ![Development](https://img.shields.io/badge/-development-blue) | string | The category of principal that performed the action. | `user`; `service`; `system` | + +**[2] `audit.actor.id`:** This SHOULD be an opaque, durable identifier (e.g. a user-UUID, service account name, or IAM principal ARN) that remains stable across sessions. Avoid using mutable display names or email addresses as the primary ID. + +**[3] `audit.actor.name`:** MAY be a login name, email address, or display name. This value is informational only; use `audit.actor.id` for stable identity correlation. + +--- + +`audit.actor.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +| --- | --- | --- | +| `service` | An automated service or daemon acting on its own behalf. | ![Development](https://img.shields.io/badge/-development-blue) | +| `system` | An internal system component (scheduler, GC, background job) that is not externally authenticated. | ![Development](https://img.shields.io/badge/-development-blue) | +| `user` | A human user authenticated to the system. | ![Development](https://img.shields.io/badge/-development-blue) | + +## Audit Integrity Attributes + +Attributes that provide cryptographic evidence that the audit record has not been tampered with since it was emitted. + +**Attributes:** + +| Key | Stability | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | +| `audit.integrity.algorithm` | ![Development](https://img.shields.io/badge/-development-blue) | string | The algorithm used to compute `audit.integrity.value`. [4] | `ES256`; `EdDSA`; `HMAC-SHA256` | +| `audit.integrity.canonicalization` | ![Development](https://img.shields.io/badge/-development-blue) | string | The canonicalization scheme applied to the record before signing or MACing. [5] | `jcs` | +| `audit.integrity.certificate` | ![Development](https://img.shields.io/badge/-development-blue) | string | A reference to the key or certificate used for `audit.integrity.value`. [6] | `key-2024-01`; `SHA256:ab12cd34...` | +| `audit.integrity.signer` | ![Development](https://img.shields.io/badge/-development-blue) | string | Identifies which tier produced the integrity proof in `audit.integrity.value`. [7] | `producer`; `collector` | +| `audit.integrity.value` | ![Development](https://img.shields.io/badge/-development-blue) | string | base64-encoded cryptographic signature or MAC covering this record. [8] | `SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c` | + +**[4] `audit.integrity.algorithm`:** MUST be set as a Resource attribute whenever `audit.integrity.value` is present on any record emitted by this resource. Use a JWA identifier (RFC 7518) for asymmetric signatures (e.g. `ES256`, `RS256`, `EdDSA`) or an IANA MAC Algorithm identifier for symmetric MACs (e.g. `HMAC-SHA256`). + +**[5] `audit.integrity.canonicalization`:** Defaults to `jcs` (RFC 8785 JSON Canonicalization Scheme). Set explicitly when any other canonicalization is used so that verifiers can reproduce the exact byte sequence that was signed. MUST be omitted when `jcs` is used. + +**[6] `audit.integrity.certificate`:** MUST be set as a Resource attribute (together with `audit.integrity.algorithm`) whenever `audit.integrity.value` is present on any record emitted by this resource. Because a service instance uses a single signing key for its entire lifetime, both `audit.integrity.algorithm` and `audit.integrity.certificate` are constant across all records from the same resource and therefore belong on the Resource, not on individual records. +Acceptable forms (in order of preference): a Key ID / `kid` (JOSE header parameter), a DER-encoded X.509 certificate base64-encoded, an X.509 certificate fingerprint (SHA-256 hex), or an Issuer + Serial Number pair. Receivers MUST NOT use this field alone for trust decisions; key validation MUST be performed out-of-band. + +**[7] `audit.integrity.signer`:** `producer` means the SDK computed the value before export. `collector` means a Tier-2 Collector signed or re-signed the record after receiving it. When absent, `producer` SHOULD be assumed. + +**[8] `audit.integrity.value`:** The input to the signing / MAC operation MUST be the canonical serialization of the `AuditRecord` with all `audit.integrity.*` attributes excluded. The default canonicalization is JCS (RFC 8785); set `audit.integrity.canonicalization` when a different scheme is used. `audit.integrity.algorithm` MUST be set on the emitting Resource whenever this attribute is present. + +## Audit Record Attributes + +Attributes that uniquely identify and version an audit record. + +**Attributes:** + +| Key | Stability | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | +| `audit.record.id` | ![Development](https://img.shields.io/badge/-development-blue) | string | A stable, globally unique identifier for this audit record. [9] | `3fa85f64-5717-4562-b3fc-2c963f66afa6` | +| `audit.schema.version` | ![Development](https://img.shields.io/badge/-development-blue) | string | The version of the audit semantic conventions schema used by this record. [10] | `1.0.0`; `1.2.0` | + +**[9] `audit.record.id`:** The SDK MUST auto-generate a UUID v4 when the caller omits this field. The value MUST remain identical across all retries of the same record. Records with the same `audit.record.id` and identical payload hash are treated as idempotent duplicates by compliant sinks. + +**[10] `audit.schema.version`:** Follows semantic versioning (MAJOR.MINOR.PATCH). Receivers MAY use this value to select the matching validation schema for the record's attribute vocabulary. + +## Audit Sequence Attributes + +Attributes that link audit records into an ordered, tamper-evident chain within a single stream. + +**Attributes:** + +| Key | Stability | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | +| `audit.sequence.end` | ![Development](https://img.shields.io/badge/-development-blue) | boolean | Set to `true` on the last record of a gracefully closed audit stream. [11] | `true` | +| `audit.sequence.number` | ![Development](https://img.shields.io/badge/-development-blue) | int | A monotonically increasing counter assigned to each record within a single audit stream. [12] | `1`; `42`; `1000001` | +| `audit.sequence.previous_hash` | ![Development](https://img.shields.io/badge/-development-blue) | string | SHA-256 hex digest of the `IntegrityHash` field of the immediately preceding record in the same audit stream. [13] | `a3f1c2e4b5d6a7f8e9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2` | +| `audit.sequence.previous_record_id` | ![Development](https://img.shields.io/badge/-development-blue) | string | The `audit.record.id` of the immediately preceding record in the same audit stream. [14] | `3fa85f64-5717-4562-b3fc-2c963f66afa6` | +| `audit.sequence.stream_id` | ![Development](https://img.shields.io/badge/-development-blue) | string | An opaque identifier that scopes the hash chain to a single audit stream. [15] | `7c9e6679-7425-40de-944b-e07fc1f90ae7` | + +**[11] `audit.sequence.end`:** Emitted by the SDK during `ForceFlush` or `Shutdown` to mark the terminal record of a stream. Absence is normal for all intermediate records — do not treat absence as an error. + +**[12] `audit.sequence.number`:** Compliant sinks and Tier-2 Collectors MUST verify that consecutive records within the same stream have strictly increasing sequence numbers. Gaps MUST trigger a gap-detection warning and a security event log entry. The counter SHOULD start at 1 and increment by 1 per emitted record, but implementations MAY use a larger step if records can be emitted concurrently from multiple threads. + +**[13] `audit.sequence.previous_hash`:** Including the previous record's sink-acknowledged hash in the current record creates an append-only hash chain. Any retroactive modification of a record is detectable by re-verifying the chain. MUST be omitted on the genesis (first) record of a stream — absence is the normative signal; no magic sentinel constant is required. + +**[14] `audit.sequence.previous_record_id`:** Provides a resolvable locator for the predecessor across shards, storage systems, and retention boundaries, complementing `audit.sequence.previous_hash`. MUST be omitted on the genesis (first) record of a stream. + +**[15] `audit.sequence.stream_id`:** The SDK SHOULD generate a UUID v4 once per `AuditLogger` instance and include it on every record emitted by that logger. + +## Audit Source Attributes + +Attributes that describe the network origin or calling context of the action. + +**Attributes:** + +| Key | Stability | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | +| `audit.source.id` | ![Development](https://img.shields.io/badge/-development-blue) | string | A stable identifier for the source system or network endpoint that originated the action. [16] | `192.0.2.42`; `device-uuid-abcd1234` | +| `audit.source.type` | ![Development](https://img.shields.io/badge/-development-blue) | string | The category of origin that initiated the action. [17] | `IPv4`; `IPv6`; `hostname` | + +**[16] `audit.source.id`:** MAY be an IP address, a device UUID, or a service mesh endpoint ID. Prefer stable identifiers (device UUID) over ephemeral ones (IP address) when both are available. + +**[17] `audit.source.type`:** Well-known values: `ipv4`, `ipv6`, `hostname`. Custom values MAY be used. + +## Audit Target Attributes + +Attributes that describe the resource or object that was acted upon. + +**Attributes:** + +| Key | Stability | Value Type | Description | Example Values | +| --- | --- | --- | --- | --- | +| `audit.target.id` | ![Development](https://img.shields.io/badge/-development-blue) | string | The stable identifier of the resource that was the target of the action. [18] | `document-42`; `arn:aws:s3:::my-bucket/key`; `/api/v1/users/9f81` | +| `audit.target.name` | ![Development](https://img.shields.io/badge/-development-blue) | string | A human-readable name or label of the target resource. [19] | `production-deploy`; `Q1-invoice-7734` | +| `audit.target.type` | ![Development](https://img.shields.io/badge/-development-blue) | string | The type or resource kind of the target. [20] | `User`; `apps/v1/Deployment`; `finance.invoice` | + +**[18] `audit.target.id`:** SHOULD be the primary key, ARN, URI, or equivalent durable identifier of the resource. When acting on a collection rather than a single resource, this MAY contain the collection identifier. + +**[19] `audit.target.name`:** Informational only. Use `audit.target.id` for stable identity correlation. + +**[20] `audit.target.type`:** SHOULD use a fully-qualified type where available (e.g. a Kubernetes GroupVersionResource or an IAM resource type). For domain-specific types a short qualified name MAY be used (e.g. `finance.invoice`). diff --git a/model/audit/logs.yaml b/model/audit/logs.yaml new file mode 100644 index 0000000000..625505c4dd --- /dev/null +++ b/model/audit/logs.yaml @@ -0,0 +1,126 @@ +groups: + # --------------------------------------------------------------------------- + # Base audit record attribute group — shared mandatory + optional attributes + # referenced by all audit event definitions. + # --------------------------------------------------------------------------- + - id: audit.record + type: attribute_group + brief: > + Attributes for an audit record emitted through the OTel Audit Logging + signal. An AuditRecord IS a LogRecord; all audit-specific data is carried + as attributes in the `audit.*` namespace. + note: > + The five mandatory attributes (`audit.record.id`, `audit.actor.id`, + `audit.actor.type`, `audit.action`, `audit.outcome`) MUST be present on + every audit record. The SDK MUST auto-generate `audit.record.id` if the + caller omits it. Absence of any other mandatory attribute is a hard error; + the SDK MUST NOT silently drop the record. + + The LogRecord fields used by audit records are: + + * `Timestamp` (MUST) — time the auditable action occurred. + * `ObservedTimestamp` (MUST) — set by SDK if omitted. + * `EventName` (MUST) — e.g. `audit.user.login`, `audit.config.change`. + * `Body` (MAY) — free-form human-readable description; MUST NOT duplicate + mandatory attributes. + * `SeverityNumber` (SHOULD NOT) — audit records are compliance evidence, not + operational diagnostics. Severity implies triage priority, which has no + meaning for records that MUST all be delivered regardless of their outcome. + Receivers MUST NOT use `SeverityNumber` to filter or prioritize audit records. + Use `audit.outcome` to distinguish success from failure. + * `InstrumentationScope` (MUST be left empty). + attributes: + # --- mandatory --- + - ref: audit.record.id + requirement_level: required + - ref: audit.actor.id + requirement_level: required + - ref: audit.actor.type + requirement_level: required + - ref: audit.action + requirement_level: required + - ref: audit.outcome + requirement_level: required + + # --- actor (optional enrichment) --- + - ref: audit.actor.name + requirement_level: opt_in + + # --- target --- + - ref: audit.target.id + requirement_level: + recommended: > + When the action is performed on a specific, identifiable resource. + - ref: audit.target.type + requirement_level: + recommended: > + When `audit.target.id` is present and the resource kind adds + meaningful context for audit review. + - ref: audit.target.name + requirement_level: opt_in + + # --- source / origin --- + - ref: audit.source.id + requirement_level: + recommended: > + When the network origin or calling device of the action is known + and meaningful for compliance review (e.g. PCI-DSS access logging). + - ref: audit.source.type + requirement_level: + recommended: When `audit.source.id` is present. + + # --- integrity (tamper-evidence) --- + - ref: audit.integrity.value + requirement_level: opt_in + - ref: audit.integrity.signer + requirement_level: + recommended: When `audit.integrity.value` is present. + - ref: audit.integrity.canonicalization + requirement_level: + conditionally_required: > + Required when a canonicalization scheme other than `jcs` (RFC 8785) + is used. Omit when `jcs` is used. + - ref: audit.integrity.algorithm + requirement_level: + conditionally_required: > + Required (as a Resource attribute) when `audit.integrity.value` is + present on any record emitted from this resource. + - ref: audit.integrity.certificate + requirement_level: + conditionally_required: > + Required (as a Resource attribute, together with + `audit.integrity.algorithm`) when `audit.integrity.value` is present + on any record emitted from this resource. + + # --- hash-chain ordering --- + - ref: audit.sequence.stream_id + requirement_level: + recommended: > + When the emitting service uses hash-chain ordering. Scopes the chain + to a single `AuditLogger` instance. + - ref: audit.sequence.number + requirement_level: + recommended: > + When the emitting service maintains a monotonic per-stream counter. + Required when `audit.sequence.previous_hash` is present. + - ref: audit.sequence.previous_hash + requirement_level: + recommended: > + When the emitting service implements a hash chain for tamper + detection. Requires `audit.sequence.number` to be present. + MUST be omitted on the genesis record of a stream. + - ref: audit.sequence.previous_record_id + requirement_level: + recommended: > + When `audit.sequence.previous_hash` is present. Provides a resolvable + locator for the predecessor record across storage boundaries. + MUST be omitted on the genesis record of a stream. + - ref: audit.sequence.end + requirement_level: + conditionally_required: > + Required on the final record of a gracefully closed stream + (emitted during `ForceFlush` or `Shutdown`). Omit on all other records. + + # --- schema versioning --- + - ref: audit.schema.version + requirement_level: opt_in diff --git a/model/audit/registry.yaml b/model/audit/registry.yaml new file mode 100644 index 0000000000..132d859c53 --- /dev/null +++ b/model/audit/registry.yaml @@ -0,0 +1,383 @@ +groups: + # --------------------------------------------------------------------------- + # audit.record — identity and deduplication + # --------------------------------------------------------------------------- + - id: registry.audit.record + type: attribute_group + display_name: Audit Record Attributes + brief: > + Attributes that uniquely identify and version an audit record. + attributes: + - id: audit.record.id + type: string + stability: development + brief: > + A stable, globally unique identifier for this audit record. + note: > + The SDK MUST auto-generate a UUID v4 when the caller omits this field. + The value MUST remain identical across all retries of the same record. + Records with the same `audit.record.id` and identical payload hash are + treated as idempotent duplicates by compliant sinks. + examples: + - "3fa85f64-5717-4562-b3fc-2c963f66afa6" + + - id: audit.schema.version + type: string + stability: development + brief: > + The version of the audit semantic conventions schema used by this record. + note: > + Follows semantic versioning (MAJOR.MINOR.PATCH). Receivers MAY use this + value to select the matching validation schema for the record's attribute + vocabulary. + examples: ["1.0.0", "1.2.0"] + + # --------------------------------------------------------------------------- + # audit.actor — who performed the action + # --------------------------------------------------------------------------- + - id: registry.audit.actor + type: attribute_group + display_name: Audit Actor Attributes + brief: > + Attributes that identify the principal (human user, service, or automated + system component) that initiated the auditable action. + attributes: + - id: audit.actor.id + type: string + stability: development + brief: > + The stable identifier of the actor that performed the action. + note: > + This SHOULD be an opaque, durable identifier (e.g. a user-UUID, service + account name, or IAM principal ARN) that remains stable across sessions. + Avoid using mutable display names or email addresses as the primary ID. + examples: + - "u8472" + - "svc-deployer" + - "arn:aws:iam::123456789012:role/DeployRole" + + - id: audit.actor.type + type: + members: + - id: user + value: "user" + brief: A human user authenticated to the system. + stability: development + - id: service + value: "service" + brief: An automated service or daemon acting on its own behalf. + stability: development + - id: system + value: "system" + brief: > + An internal system component (scheduler, GC, background job) that + is not externally authenticated. + stability: development + stability: development + brief: > + The category of principal that performed the action. + examples: ["user", "service", "system"] + + - id: audit.actor.name + type: string + stability: development + brief: > + A human-readable display name or username of the actor. + note: > + MAY be a login name, email address, or display name. This value is + informational only; use `audit.actor.id` for stable identity correlation. + examples: ["alice", "alice@example.com", "Alice Smith"] + + # --------------------------------------------------------------------------- + # audit.action — what was done + # --------------------------------------------------------------------------- + - id: registry.audit.action + type: attribute_group + display_name: Audit Action Attributes + brief: > + Attributes that describe the operation performed by the actor. + attributes: + - id: audit.action + type: string + stability: development + brief: > + The auditable operation that was performed. + note: > + SHOULD be an uppercase verb from a controlled vocabulary where possible. + Well-known values include `LOGIN`, `LOGOUT`, `READ`, `CREATE`, `UPDATE`, + `DELETE`, `GRANT`, `REVOKE`, `EXPORT`, `IMPORT`, `EXECUTE`, `APPROVE`, + `REJECT`. Custom values MAY be used when none of the well-known values + applies, but SHOULD be documented in the producing service's changelog. + examples: + - "LOGIN" + - "DELETE" + - "GRANT" + - "CONFIG_CHANGE" + + - id: audit.outcome + type: + members: + - id: success + value: "success" + brief: > + The action completed successfully and produced its intended effect. + stability: development + - id: failure + value: "failure" + brief: > + The action was attempted but did not complete successfully (e.g. + authorization denied, validation error, resource not found). + stability: development + - id: unknown + value: "unknown" + brief: > + The outcome could not be determined at the time the event was + emitted, for example because the operation is async. + stability: development + stability: development + brief: > + The result of the auditable action. + examples: ["success", "failure", "unknown"] + + # --------------------------------------------------------------------------- + # audit.target — the resource acted upon + # --------------------------------------------------------------------------- + - id: registry.audit.target + type: attribute_group + display_name: Audit Target Attributes + brief: > + Attributes that describe the resource or object that was acted upon. + attributes: + - id: audit.target.id + type: string + stability: development + brief: > + The stable identifier of the resource that was the target of the action. + note: > + SHOULD be the primary key, ARN, URI, or equivalent durable identifier + of the resource. When acting on a collection rather than a single + resource, this MAY contain the collection identifier. + examples: + - "document-42" + - "arn:aws:s3:::my-bucket/key" + - "/api/v1/users/9f81" + + - id: audit.target.type + type: string + stability: development + brief: > + The type or resource kind of the target. + note: > + SHOULD use a fully-qualified type where available (e.g. a Kubernetes + GroupVersionResource or an IAM resource type). For domain-specific types + a short qualified name MAY be used (e.g. `finance.invoice`). + examples: + - "User" + - "apps/v1/Deployment" + - "finance.invoice" + + - id: audit.target.name + type: string + stability: development + brief: > + A human-readable name or label of the target resource. + note: > + Informational only. Use `audit.target.id` for stable identity correlation. + examples: + - "production-deploy" + - "Q1-invoice-7734" + + # --------------------------------------------------------------------------- + # audit.source — network / origin of the action + # --------------------------------------------------------------------------- + - id: registry.audit.source + type: attribute_group + display_name: Audit Source Attributes + brief: > + Attributes that describe the network origin or calling context of the action. + attributes: + - id: audit.source.id + type: string + stability: development + brief: > + A stable identifier for the source system or network endpoint that + originated the action. + note: > + MAY be an IP address, a device UUID, or a service mesh endpoint ID. + Prefer stable identifiers (device UUID) over ephemeral ones (IP address) + when both are available. + examples: + - "192.0.2.42" + - "device-uuid-abcd1234" + + - id: audit.source.type + type: string + stability: development + brief: > + The category of origin that initiated the action. + note: > + Well-known values: `ipv4`, `ipv6`, `hostname`. + Custom values MAY be used. + examples: + - "IPv4" + - "IPv6" + - "hostname" + + # --------------------------------------------------------------------------- + # audit.integrity — cryptographic tamper-evidence + # --------------------------------------------------------------------------- + - id: registry.audit.integrity + type: attribute_group + display_name: Audit Integrity Attributes + brief: > + Attributes that provide cryptographic evidence that the audit record has + not been tampered with since it was emitted. + attributes: + - id: audit.integrity.value + type: string + stability: development + brief: > + base64-encoded cryptographic signature or MAC covering this record. + note: > + The input to the signing / MAC operation MUST be the canonical + serialization of the `AuditRecord` with all `audit.integrity.*` + attributes excluded. The default canonicalization is JCS (RFC 8785); + set `audit.integrity.canonicalization` when a different scheme is used. + `audit.integrity.algorithm` MUST be set on the emitting Resource + whenever this attribute is present. + examples: + - "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" + + - id: audit.integrity.signer + type: string + stability: development + brief: > + Identifies which tier produced the integrity proof in `audit.integrity.value`. + note: > + `producer` means the SDK computed the value before export. `collector` + means a Tier-2 Collector signed or re-signed the record after receiving it. + When absent, `producer` SHOULD be assumed. + examples: ["producer", "collector"] + + - id: audit.integrity.canonicalization + type: string + stability: development + brief: > + The canonicalization scheme applied to the record before signing or MACing. + note: > + Defaults to `jcs` (RFC 8785 JSON Canonicalization Scheme). Set explicitly + when any other canonicalization is used so that verifiers can reproduce the + exact byte sequence that was signed. MUST be omitted when `jcs` is used. + examples: ["jcs"] + + - id: audit.integrity.algorithm + type: string + stability: development + brief: > + The algorithm used to compute `audit.integrity.value`. + note: > + MUST be set as a Resource attribute whenever `audit.integrity.value` is + present on any record emitted by this resource. Use a JWA identifier + (RFC 7518) for asymmetric signatures (e.g. `ES256`, `RS256`, `EdDSA`) or + an IANA MAC Algorithm identifier for symmetric MACs (e.g. `HMAC-SHA256`). + examples: + - "ES256" + - "EdDSA" + - "HMAC-SHA256" + + - id: audit.integrity.certificate + type: string + stability: development + brief: > + A reference to the key or certificate used for `audit.integrity.value`. + note: > + MUST be set as a Resource attribute (together with + `audit.integrity.algorithm`) whenever `audit.integrity.value` is present + on any record emitted by this resource. Because a service instance uses a + single signing key for its entire lifetime, both `audit.integrity.algorithm` + and `audit.integrity.certificate` are constant across all records from the + same resource and therefore belong on the Resource, not on individual records. + + Acceptable forms (in order of preference): a Key ID / `kid` (JOSE header + parameter), a DER-encoded X.509 certificate base64-encoded, an X.509 + certificate fingerprint (SHA-256 hex), or an Issuer + Serial Number pair. + Receivers MUST NOT use this field alone for trust decisions; key + validation MUST be performed out-of-band. + examples: + - "key-2024-01" + - "SHA256:ab12cd34..." + + # --------------------------------------------------------------------------- + # audit.sequence — hash-chain ordering + # --------------------------------------------------------------------------- + - id: registry.audit.sequence + type: attribute_group + display_name: Audit Sequence Attributes + brief: > + Attributes that link audit records into an ordered, tamper-evident chain + within a single stream. + attributes: + - id: audit.sequence.number + type: int + stability: development + brief: > + A monotonically increasing counter assigned to each record within a + single audit stream. + note: > + Compliant sinks and Tier-2 Collectors MUST verify that consecutive + records within the same stream have strictly increasing sequence numbers. + Gaps MUST trigger a gap-detection warning and a security event log entry. + The counter SHOULD start at 1 and increment by 1 per emitted record, but + implementations MAY use a larger step if records can be emitted + concurrently from multiple threads. + examples: [1, 42, 1000001] + + - id: audit.sequence.previous_hash + type: string + stability: development + brief: > + SHA-256 hex digest of the `IntegrityHash` field of the immediately + preceding record in the same audit stream. + note: > + Including the previous record's sink-acknowledged hash in the current + record creates an append-only hash chain. Any retroactive modification + of a record is detectable by re-verifying the chain. MUST be omitted on + the genesis (first) record of a stream — absence is the normative signal; + no magic sentinel constant is required. + examples: + - "a3f1c2e4b5d6a7f8e9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2" + + - id: audit.sequence.previous_record_id + type: string + stability: development + brief: > + The `audit.record.id` of the immediately preceding record in the same + audit stream. + note: > + Provides a resolvable locator for the predecessor across shards, storage + systems, and retention boundaries, complementing `audit.sequence.previous_hash`. + MUST be omitted on the genesis (first) record of a stream. + examples: + - "3fa85f64-5717-4562-b3fc-2c963f66afa6" + + - id: audit.sequence.end + type: boolean + stability: development + brief: > + Set to `true` on the last record of a gracefully closed audit stream. + note: > + Emitted by the SDK during `ForceFlush` or `Shutdown` to mark the terminal + record of a stream. Absence is normal for all intermediate records — do not + treat absence as an error. + examples: [true] + + - id: audit.sequence.stream_id + type: string + stability: development + brief: > + An opaque identifier that scopes the hash chain to a single audit stream. + note: > + The SDK SHOULD generate a UUID v4 once per `AuditLogger` instance and + include it on every record emitted by that logger. + examples: + - "7c9e6679-7425-40de-944b-e07fc1f90ae7"