guildhouse-spire-plugins/specs/shellstream-extensions.md

491 lines
18 KiB
Markdown

# Shellstream SSH Certificate Extensions
## 1. Abstract
Shellstream extensions are a set of SSH certificate extensions using the
`@guildhouse.io` vendor suffix that carry structured governance metadata
within SSH certificates issued by SPIRE. These extensions encode
authorization scope, tenant context, governance ceremony references, and
merkle audit proofs, enabling SSH servers to make fine-grained
authorization decisions based on Guildhouse platform state without
requiring separate API calls at connection time.
## 2. Status
**Draft Specification** -- This document is a working draft and subject to
change. It defines the normative behavior that conforming implementations
MUST follow once finalized.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
document are to be interpreted as described in RFC 2119.
## 3. Terminology
**Shellstream Extension**
: An SSH certificate extension whose name uses the `@guildhouse.io`
vendor suffix and whose value carries Guildhouse governance metadata as
defined in this specification.
**SSH Certificate Extension**
: A key-value string pair embedded in an OpenSSH certificate as defined
in the OpenSSH PROTOCOL.certkeys document. Extensions are advisory and
do not restrict the certificate holder; they convey additional
information to the server.
**Vendor Suffix**
: The `@domain` portion of an extension name that designates the
namespace owner, following the OpenSSH convention for avoiding
collisions between independently defined extensions.
**SAT (Substrate Attestation Token)**
: A signed token issued by the Quartermaster credential service that
binds a SPIFFE identity to a set of permitted operations on a specific
registry type. The SAT is the primary authorization artifact in the
Guildhouse platform.
**SatScope**
: A structured object within a SAT that defines the registry type,
permitted verbs, and resource pattern for which the token grants
access.
**Governance Ceremony**
: A structured approval workflow managed by the Bascule CeremonyService
that authorizes privilege elevation. Ceremony types range from
self-granted to quorum-based approval.
**Merkle Anchor**
: A cryptographic commitment (merkle tree root hash) representing the
complete governance state at a point in time. Each governance mutation
is recorded as a leaf in the tree.
**MutationEnvelope**
: A signed wrapper around a governance state change that is recorded as a
leaf in the governance merkle tree.
**Accord Policy**
: A tenant-scoped authorization policy evaluated by the Accord policy
engine (backed by OPA) that defines roles, permissions, and ceremony
requirements for a tenant.
**Trust Domain**
: A SPIFFE trust domain (e.g., `guildhouse.io`) that defines the
boundary within which SPIFFE identities and their associated
attestations are valid.
## 4. Introduction
SSH certificates, as defined by OpenSSH's PROTOCOL.certkeys, support
arbitrary extensions as key-value string pairs. By convention, vendor-
specific extensions use the `name@domain` format to avoid collisions with
extensions defined by other parties or by future OpenSSH releases.
Shellstream extensions embed Guildhouse governance metadata into SSH
certificates so that SSH servers can make authorization decisions based on
platform state without issuing separate API calls. When SPIRE issues an
SSH certificate for a Guildhouse workload or user, the certificate may
include Shellstream extensions that describe:
- **What** the holder is authorized to do (SAT scope).
- **Who** the holder is acting as (tenant identity, roles).
- **Why** elevated access was granted (ceremony reference).
- **When** the governance state was captured (merkle root, epoch).
This decoupling of authorization metadata from runtime API lookups enables
offline authorization decisions, improves latency, and creates a
cryptographically verifiable audit trail linking SSH sessions to
governance state.
## 5. Extension Format
### 5.1 Naming Convention
All Shellstream extensions use the `@guildhouse.io` vendor suffix.
Extension names MUST be lowercase, hyphen-separated identifiers followed
by the suffix:
```
<name>@guildhouse.io
```
where `<name>` matches the regular expression `[a-z][a-z0-9]*(-[a-z0-9]+)*`.
### 5.2 Value Encoding
Extension values are UTF-8 strings, as constrained by the SSH certificate
extension format (RFC 4251 string type). All values MUST be valid UTF-8.
Binary data MUST be encoded as follows:
- Cryptographic hashes: lowercase hexadecimal encoding.
- Proofs and opaque binary payloads: base64 encoding using the standard
alphabet with padding (RFC 4648 Section 4).
JSON-structured values MUST use compact encoding with no unnecessary
whitespace (no spaces after colons or commas, no newlines or
indentation).
## 6. Extension Registry
This section defines each Shellstream extension. Extensions marked
REQUIRED MUST be present in every Shellstream-bearing SSH certificate.
Extensions marked OPTIONAL MAY be omitted.
### 6.1 `sat-scope@guildhouse.io`
**Presence:** REQUIRED when a SAT is associated with the certificate.
**Value:** A JSON object (or JSON array of objects when multiple scopes
exist) with the following structure:
```json
{"registry_type":"<type>","verbs":["<verb>",...],"resource_pattern":"<pattern>"}
```
**Fields:**
| Field | Type | Description |
|--------------------|-----------------|-----------------------------------------------------------------------------|
| `registry_type` | string | Identifies the registry kind. Values include `"oci"`, `"helm"`, `"git"`, `"database"`. |
| `verbs` | array of string | Permitted operations. Examples: `["read","write"]`, `["push","pull"]`. |
| `resource_pattern` | string | Glob pattern for resource matching. Examples: `"tenant-a/*"`, `"ns/repo:*"`. |
When a single SAT scope exists, the value MUST be a single JSON object.
When multiple SAT scopes exist, the value MUST be a JSON array of scope
objects. Implementations MUST accept both forms.
**Example (single scope):**
```
sat-scope@guildhouse.io = {"registry_type":"oci","verbs":["push","pull"],"resource_pattern":"acme-corp/*"}
```
**Example (multiple scopes):**
```
sat-scope@guildhouse.io = [{"registry_type":"oci","verbs":["pull"],"resource_pattern":"acme-corp/*"},{"registry_type":"helm","verbs":["read"],"resource_pattern":"charts/*"}]
```
### 6.2 `sat-hash@guildhouse.io`
**Presence:** REQUIRED when a SAT is associated with the certificate.
**Value:** Lowercase hex-encoded SHA-256 hash of the raw SAT bytes.
The value MUST be exactly 64 hexadecimal characters (`[0-9a-f]{64}`).
This hash is used for audit correlation -- it links the SSH session to the
SAT without embedding the SAT itself in the certificate.
**Example:**
```
sat-hash@guildhouse.io = a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2
```
### 6.3 `tenant-id@guildhouse.io`
**Presence:** REQUIRED.
**Value:** UUID string formatted per RFC 4122, using lowercase hexadecimal
digits with hyphens in the 8-4-4-4-12 grouping.
The value MUST match the regular expression:
```
[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}
```
This extension identifies the tenant context for the SSH session. All
authorization decisions MUST be scoped to this tenant.
**Example:**
```
tenant-id@guildhouse.io = 7b2a91c4-3f8e-4d12-b5a6-9c0e1d2f3a4b
```
### 6.4 `roles@guildhouse.io`
**Presence:** REQUIRED.
**Value:** Comma-separated list of role names with no whitespace. Each
role name MUST match `[a-z][a-z0-9_]*`.
Roles are defined by the tenant's Accord policy and represent the set of
permissions the certificate holder has been granted for this session.
**Example:**
```
roles@guildhouse.io = analyst,viewer
```
```
roles@guildhouse.io = administrator
```
### 6.5 `ceremony-id@guildhouse.io`
**Presence:** OPTIONAL -- present only for elevated sessions that were
authorized through a governance ceremony.
**Value:** UUID string formatted per RFC 4122, using the same format
specified in Section 6.3.
This extension references the governance ceremony that authorized the
privilege elevation for this session.
**Example:**
```
ceremony-id@guildhouse.io = e4f5a6b7-8c9d-0e1f-2a3b-4c5d6e7f8a9b
```
### 6.6 `ceremony-type@guildhouse.io`
**Presence:** OPTIONAL -- MUST be present when `ceremony-id@guildhouse.io`
is present. MUST NOT be present when `ceremony-id@guildhouse.io` is
absent.
**Value:** One of the following string literals, corresponding to the
`CeremonyType` enum from the Bascule CeremonyService:
| Value | Description |
|-------------------------|---------------------------------------------------------------------|
| `self_grant` | The holder approved their own elevation (permitted by policy). |
| `single_approval` | A single designated approver authorized the elevation. |
| `quorum_approval` | A quorum of approvers collectively authorized the elevation. |
| `emergency_break_glass` | Emergency access granted outside normal approval flow, with enhanced audit. |
**Example:**
```
ceremony-type@guildhouse.io = quorum_approval
```
### 6.7 `merkle-root@guildhouse.io`
**Presence:** OPTIONAL.
**Value:** Lowercase hex-encoded SHA-256 merkle root hash. The value MUST
be exactly 64 hexadecimal characters (`[0-9a-f]{64}`).
This is the root of the governance merkle tree at the time the certificate
was issued. It allows offline verification that the certificate was issued
during a known governance state.
**Example:**
```
merkle-root@guildhouse.io = 4d7a9c2e1f3b5a8d0e6c4b2a9f7e5d3c1b0a8f6e4d2c0b9a7f5e3d1c0b8a7f
```
### 6.8 `merkle-proof@guildhouse.io`
**Presence:** OPTIONAL -- MUST be present only when
`merkle-root@guildhouse.io` is present. MAY be omitted even when
`merkle-root` is present (the root alone is useful for epoch pinning).
**Value:** Base64-encoded (standard alphabet with padding, per RFC 4648
Section 4) serialized inclusion proof.
**Proof wire format:** The proof is a byte sequence consisting of
concatenated 32-byte SHA-256 sibling hashes followed by a single final
byte encoding the direction bits. Each bit in the direction byte indicates
whether the corresponding sibling is on the left (0) or right (1) of the
path, with the least-significant bit corresponding to the first sibling.
This limits proofs to trees of depth 8 (256 leaves). For deeper trees, a
multi-byte direction encoding will be specified in a future revision.
**Example:**
```
merkle-proof@guildhouse.io = QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ehQ=
```
### 6.9 `governance-epoch@guildhouse.io`
**Presence:** OPTIONAL.
**Value:** Decimal string representation of an unsigned 64-bit integer.
The value MUST match `[1-9][0-9]*|0` (no leading zeros except for the
value zero itself).
The governance epoch is a monotonically increasing counter that increments
with each governance state change. It enables stale-state detection
without requiring full merkle verification: if a server's last-known epoch
is greater than the certificate's epoch, the certificate was issued
against stale governance state.
**Example:**
```
governance-epoch@guildhouse.io = 42
```
## 7. Encoding Rules
The following encoding rules apply to all Shellstream extension values:
1. All values MUST be valid UTF-8 (RFC 3629).
2. Hash values (in `sat-hash`, `merkle-root`) MUST be lowercase
hexadecimal (`[0-9a-f]+`). Uppercase hex digits MUST be rejected.
3. Base64 values (in `merkle-proof`) MUST use the standard alphabet
(`A-Z`, `a-z`, `0-9`, `+`, `/`) with `=` padding as specified in
RFC 4648 Section 4. URL-safe base64 MUST NOT be used.
4. JSON values (in `sat-scope`) MUST use compact encoding: no spaces
after `:` or `,`, no newlines, no indentation. Parsers SHOULD accept
non-compact JSON for robustness but generators MUST produce compact
JSON.
5. UUID values (in `tenant-id`, `ceremony-id`) MUST be lowercase with
hyphens in 8-4-4-4-12 format. Uppercase UUID strings MUST be
rejected.
6. Numeric values (in `governance-epoch`) MUST be decimal strings
without leading zeros (except for the literal `"0"`).
7. Comma-separated values (in `roles`) MUST NOT contain whitespace
between items.
## 8. Validation Requirements
Implementations receiving SSH certificates with Shellstream extensions
MUST validate the following constraints before using extension values for
authorization or audit purposes:
### 8.1 Structural Validation
- Each extension value MUST conform to the format specified in its
registry entry (Section 6).
- Values that fail format validation MUST be treated as absent. The
server MAY log a warning but MUST NOT terminate the SSH connection
solely due to a malformed extension value.
### 8.2 Co-occurrence Constraints
The following co-occurrence rules MUST be enforced:
| If present | Then MUST also be present |
|-------------------------------|--------------------------------|
| `sat-scope@guildhouse.io` | `sat-hash@guildhouse.io` |
| `sat-hash@guildhouse.io` | `sat-scope@guildhouse.io` |
| `ceremony-id@guildhouse.io` | `ceremony-type@guildhouse.io` |
| `ceremony-type@guildhouse.io` | `ceremony-id@guildhouse.io` |
| `merkle-proof@guildhouse.io` | `merkle-root@guildhouse.io` |
Note: `merkle-root@guildhouse.io` MAY be present without
`merkle-proof@guildhouse.io` (root-only pinning).
### 8.3 Required Extensions
The following extensions MUST always be present in a Shellstream-bearing
SSH certificate:
- `tenant-id@guildhouse.io`
- `roles@guildhouse.io`
A certificate that contains any `@guildhouse.io` extension but is missing
either `tenant-id` or `roles` MUST be treated as invalid. The server
SHOULD reject the session.
### 8.4 Forward Compatibility
Unknown extensions bearing the `@guildhouse.io` suffix MUST be ignored.
Implementations MUST NOT reject a certificate solely because it contains
unrecognized `@guildhouse.io` extensions. This ensures that new
extensions can be introduced without breaking existing deployments.
## 9. Security Considerations
### 9.1 Extension Visibility
SSH certificate extensions are not encrypted. Any entity that can read the
certificate (including intermediate proxies, logging infrastructure, and
the SSH server) can read extension values. Accordingly:
- Implementations MUST NOT embed secrets, bearer tokens, private keys, or
other sensitive credentials in extension values.
- The `sat-hash` extension contains a SHA-256 hash of the SAT, not the
SAT itself. The SAT MUST be validated through a separate, secure
channel.
### 9.2 Authorization Boundary
- Merkle proofs provide **auditability**, not authorization. A valid
merkle inclusion proof means the credential issuance event was recorded
in the governance tree; it does not assert that the event was correct or
authorized. Authorization decisions MUST be based on SAT scope and
Accord policy, not on merkle proof validity alone.
- The `governance-epoch` extension enables stale-state detection but MUST
NOT be used as the sole authorization signal. A stale epoch indicates
that governance state may have changed since issuance; the server
SHOULD require re-attestation in this case.
### 9.3 Parsing Safety
Extension values are strings, not executable code. Implementations MUST
NOT evaluate extension values as code, shell commands, SQL, or any other
executable format. JSON values MUST be parsed with a standard JSON parser;
hand-rolled parsers are strongly discouraged.
### 9.4 Size Constraints
The total size of all Shellstream extension values (keys and values
combined) SHOULD NOT exceed 4096 bytes (4 KB). Exceeding this limit may
cause compatibility issues with certain SSH implementations that impose
limits on certificate size. Implementations MAY reject certificates whose
total `@guildhouse.io` extension payload exceeds this threshold.
### 9.5 Replay and Freshness
SSH certificates have their own validity period (valid-after to
valid-before). Shellstream extensions inherit this validity window.
Implementations SHOULD treat extension values as valid only within the
certificate's validity period. The `governance-epoch` extension provides
an additional freshness signal beyond the certificate's temporal validity.
## 10. Compatibility
### 10.1 SSH Protocol Compatibility
All Shellstream extensions use the standard SSH certificate extension
format: a string key mapping to a string value. This is fully compatible
with OpenSSH and any SSH implementation that conforms to the
PROTOCOL.certkeys specification.
### 10.2 Transparent Degradation
SSH servers that do not understand Shellstream extensions will ignore them
entirely. This is standard OpenSSH behavior for unrecognized extensions.
Certificates bearing Shellstream extensions remain valid SSH certificates
and can be used for authentication on non-Guildhouse SSH servers (though
governance-based authorization will not be available).
### 10.3 Additive Evolution
Extensions are additive. New `@guildhouse.io` extensions can be
introduced in future revisions of this specification without breaking
existing parsers, per the forward compatibility rule in Section 8.4.
### 10.4 Version Negotiation
Version negotiation is implicit via extension presence. There is no
explicit version field. If a future revision deprecates or changes the
semantics of an extension, it MUST do so under a new extension name. The
original extension name retains its original semantics indefinitely.
## 11. References
- **OpenSSH PROTOCOL.certkeys** -- OpenSSH certificate format and
extension mechanism.
https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.certkeys
- **RFC 2119** -- Key words for use in RFCs to Indicate Requirement
Levels. Bradner, S. March 1997.
https://datatracker.ietf.org/doc/html/rfc2119
- **RFC 3629** -- UTF-8, a transformation format of ISO 10646. Yergeau,
F. November 2003.
https://datatracker.ietf.org/doc/html/rfc3629
- **RFC 4122** -- A Universally Unique IDentifier (UUID) URN Namespace.
Leach, P., Mealling, M., and R. Salz. July 2005.
https://datatracker.ietf.org/doc/html/rfc4122
- **RFC 4251** -- The Secure Shell (SSH) Protocol Architecture. Ylonen,
T. and C. Lonvick, Ed. January 2006.
https://datatracker.ietf.org/doc/html/rfc4251
- **RFC 4648** -- The Base16, Base32, and Base64 Data Encodings.
Josefsson, S. October 2006.
https://datatracker.ietf.org/doc/html/rfc4648
- **RFC 8785** -- JSON Canonicalization Scheme (JCS). Rundgren, B.,
Jordan, B., and S. Erdtman. June 2020.
https://datatracker.ietf.org/doc/html/rfc8785