Files
git.stella-ops.org/docs/ci/dsse-build-flow.md
master 7040984215 Add inline DSSE provenance documentation and Mongo schema
- Introduced a new document outlining the inline DSSE provenance for SBOM, VEX, scan, and derived events.
- Defined the Mongo schema for event patches, including key fields for provenance and trust verification.
- Documented the write path for ingesting provenance metadata and backfilling historical events.
- Created CI/CD snippets for uploading DSSE attestations and generating provenance metadata.
- Established Mongo indexes for efficient provenance queries and provided query recipes for various use cases.
- Outlined policy gates for managing VEX decisions based on provenance verification.
- Included UI nudges for displaying provenance information and implementation tasks for future enhancements.

---

Implement reachability lattice and scoring model

- Developed a comprehensive document detailing the reachability lattice and scoring model.
- Defined core types for reachability states, evidence, and mitigations with corresponding C# models.
- Established a scoring policy with base score contributions from various evidence classes.
- Mapped reachability states to VEX gates and provided a clear overview of evidence sources.
- Documented the event graph schema for persisting reachability data in MongoDB.
- Outlined the integration of runtime probes for evidence collection and defined a roadmap for future tasks.

---

Introduce uncertainty states and entropy scoring

- Created a draft document for tracking uncertainty states and their impact on risk scoring.
- Defined core uncertainty states with associated entropy values and evidence requirements.
- Established a schema for storing uncertainty states alongside findings.
- Documented the risk score calculation incorporating uncertainty and its effect on final risk assessments.
- Provided policy guidelines for handling uncertainty in decision-making processes.
- Outlined UI guidelines for displaying uncertainty information and suggested remediation actions.

---

Add Ruby package inventory management

- Implemented Ruby package inventory management with corresponding data models and storage mechanisms.
- Created C# records for Ruby package inventory, artifacts, provenance, and runtime details.
- Developed a repository for managing Ruby package inventory documents in MongoDB.
- Implemented a service for storing and retrieving Ruby package inventories.
- Added unit tests for the Ruby package inventory store to ensure functionality and data integrity.
2025-11-13 00:20:33 +02:00

280 lines
9.1 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Build-Time DSSE Attestation Walkthrough
> **Status:** Draft — aligns with the November 2025 advisory “Embed in-toto attestations (DSSE-wrapped) into .NET 10/C# builds.”
> **Owners:** Attestor Guild · DevOps Guild · Docs Guild.
This guide shows how to emit signed, in-toto compliant DSSE envelopes for every container build step (scan → package → push) using StellaOps Authority keys. The same primitives power our Signer/Attestor services, but this walkthrough targets developer pipelines (GitHub/GitLab, dotnet builds, container scanners).
---
## 1. Concepts refresher
| Term | Meaning |
|------|---------|
| **In-toto Statement** | JSON document describing what happened (predicate) to which artifact (subject). |
| **DSSE** | Dead Simple Signing Envelope: wraps the statement, base64 payload, and signatures. |
| **Authority Signer** | StellaOps client that signs data via file-based keys, HSM/KMS, or keyless Fulcio certs. |
| **PAE** | Pre-Authentication Encoding: canonical “DSSEv1 <len> <payloadType> <len> <payload>” byte layout that is signed. |
Requirements:
1. .NET 10 SDK (preview) for C# helper code.
2. Authority key material (dev: file-based Ed25519; prod: Authority/KMS signer).
3. Artifact digest (e.g., `pkg:docker/registry/app@sha256:...`) per step.
---
## 2. Helpers (drop-in library)
Create `src/StellaOps.Attestation` with:
```csharp
public sealed record InTotoStatement(
string _type,
IReadOnlyList<Subject> subject,
string predicateType,
object predicate);
public sealed record Subject(string name, IReadOnlyDictionary<string,string> digest);
public sealed record DsseEnvelope(
string payloadType,
string payload,
IReadOnlyList<Signature> signatures);
public sealed record Signature(string keyid, string sig);
public interface IAuthoritySigner
{
Task<string> GetKeyIdAsync(CancellationToken ct = default);
Task<byte[]> SignAsync(ReadOnlyMemory<byte> pae, CancellationToken ct = default);
}
```
DSSE helper:
```csharp
public static class DsseHelper
{
public static async Task<DsseEnvelope> WrapAsync(
InTotoStatement statement,
IAuthoritySigner signer,
string payloadType = "application/vnd.in-toto+json",
CancellationToken ct = default)
{
var payloadBytes = JsonSerializer.SerializeToUtf8Bytes(statement,
new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull });
var pae = PreAuthEncode(payloadType, payloadBytes);
var signature = await signer.SignAsync(pae, ct).ConfigureAwait(false);
var keyId = await signer.GetKeyIdAsync(ct).ConfigureAwait(false);
return new DsseEnvelope(
payloadType,
Convert.ToBase64String(payloadBytes),
new[] { new Signature(keyId, Convert.ToBase64String(signature)) });
}
private static byte[] PreAuthEncode(string payloadType, byte[] payload)
{
static byte[] Cat(params byte[][] parts)
{
var len = parts.Sum(p => p.Length);
var buf = new byte[len];
var offset = 0;
foreach (var part in parts)
{
Buffer.BlockCopy(part, 0, buf, offset, part.Length);
offset += part.Length;
}
return buf;
}
var header = Encoding.UTF8.GetBytes("DSSEv1");
var pt = Encoding.UTF8.GetBytes(payloadType);
var lenPt = Encoding.UTF8.GetBytes(pt.Length.ToString(CultureInfo.InvariantCulture));
var lenPayload = Encoding.UTF8.GetBytes(payload.Length.ToString(CultureInfo.InvariantCulture));
var space = Encoding.UTF8.GetBytes(" ");
return Cat(header, space, lenPt, space, pt, space, lenPayload, space, payload);
}
}
```
Authority signer examples:
/dev (file Ed25519):
```csharp
public sealed class FileEd25519Signer : IAuthoritySigner, IDisposable
{
private readonly Ed25519 _ed;
private readonly string _keyId;
public FileEd25519Signer(byte[] privateKeySeed, string keyId)
{
_ed = new Ed25519(privateKeySeed);
_keyId = keyId;
}
public Task<string> GetKeyIdAsync(CancellationToken ct) => Task.FromResult(_keyId);
public Task<byte[]> SignAsync(ReadOnlyMemory<byte> pae, CancellationToken ct)
=> Task.FromResult(_ed.Sign(pae.Span.ToArray()));
public void Dispose() => _ed.Dispose();
}
```
Prod (Authority KMS):
Reuse the existing `StellaOps.Signer.KmsSigner` adapter—wrap it behind `IAuthoritySigner`.
---
## 3. Emitting attestations per step
Subject helper:
```csharp
static Subject ImageSubject(string imageDigest) => new(
name: imageDigest,
digest: new Dictionary<string,string>{{"sha256", imageDigest.Replace("sha256:", "", StringComparison.Ordinal)}});
```
### 3.1 Scan
```csharp
var scanStmt = new InTotoStatement(
_type: "https://in-toto.io/Statement/v1",
subject: new[]{ ImageSubject(imageDigest) },
predicateType: "https://stella.ops/predicates/scanner-evidence/v1",
predicate: new {
scanner = "StellaOps.Scanner 0.9.0",
findingsSha256 = scanResultsHash,
startedAt = startedIso,
finishedAt = finishedIso,
rulePack = "lattice:default@2025-11-01"
});
var scanEnvelope = await DsseHelper.WrapAsync(scanStmt, signer);
await File.WriteAllTextAsync("artifacts/attest-scan.dsse.json", JsonSerializer.Serialize(scanEnvelope));
```
### 3.2 Package (SLSA provenance)
```csharp
var pkgStmt = new InTotoStatement(
"https://in-toto.io/Statement/v1",
new[]{ ImageSubject(imageDigest) },
"https://slsa.dev/provenance/v1",
new {
builder = new { id = "stella://builder/dockerfile" },
buildType = "dockerfile/v1",
invocation = new { configSource = repoUrl, entryPoint = dockerfilePath },
materials = new[] { new { uri = repoUrl, digest = new { git = gitSha } } }
});
var pkgEnvelope = await DsseHelper.WrapAsync(pkgStmt, signer);
await File.WriteAllTextAsync("artifacts/attest-package.dsse.json", JsonSerializer.Serialize(pkgEnvelope));
```
### 3.3 Push
```csharp
var pushStmt = new InTotoStatement(
"https://in-toto.io/Statement/v1",
new[]{ ImageSubject(imageDigest) },
"https://stella.ops/predicates/push/v1",
new { registry = registryUrl, repository = repoName, tags, pushedAt = DateTimeOffset.UtcNow });
var pushEnvelope = await DsseHelper.WrapAsync(pushStmt, signer);
await File.WriteAllTextAsync("artifacts/attest-push.dsse.json", JsonSerializer.Serialize(pushEnvelope));
```
---
## 4. CI integration
### 4.1 GitLab example
```yaml
.attest-template: &attest
image: mcr.microsoft.com/dotnet/sdk:10.0-preview
before_script:
- dotnet build src/StellaOps.Attestation/StellaOps.Attestation.csproj
variables:
AUTHORITY_KEY_FILE: "$CI_PROJECT_DIR/secrets/ed25519.key"
IMAGE_DIGEST: "$CI_REGISTRY_IMAGE@${CI_COMMIT_SHA}"
attest:scan:
stage: scan
script:
- dotnet run --project tools/StellaOps.Attestor.Tool -- step scan --subject "$IMAGE_DIGEST" --out artifacts/attest-scan.dsse.json
artifacts:
paths: [artifacts/attest-scan.dsse.json]
attest:package:
stage: package
script:
- dotnet run --project tools/StellaOps.Attestor.Tool -- step package --subject "$IMAGE_DIGEST" --out artifacts/attest-package.dsse.json
attest:push:
stage: push
script:
- dotnet run --project tools/StellaOps.Attestor.Tool -- step push --subject "$IMAGE_DIGEST" --registry "$CI_REGISTRY" --tags "$CI_COMMIT_REF_NAME"
```
### 4.2 GitHub Actions snippet
```yaml
jobs:
attest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with: { dotnet-version: '10.0.x' }
- name: Build attestor helpers
run: dotnet build src/StellaOps.Attestation/StellaOps.Attestation.csproj
- name: Emit scan attestation
run: dotnet run --project tools/StellaOps.Attestor.Tool -- step scan --subject "${{ env.IMAGE_DIGEST }}" --out artifacts/attest-scan.dsse.json
env:
AUTHORITY_KEY_REF: ${{ secrets.AUTHORITY_KEY_REF }}
```
---
## 5. Verification
* `stella attest verify --file artifacts/attest-scan.dsse.json` (CLI planned under `DSSE-CLI-401-021`).
* Manual validation:
1. Base64 decode payload → ensure `_type` = `https://in-toto.io/Statement/v1`, `subject[].digest.sha256` matches artifact.
2. Recompute PAE and verify signature with the Authority public key.
3. Attach envelope to Rekor (optional) via existing Attestor API.
---
## 6. Storage conventions
Store DSSE files next to build outputs:
```
artifacts/
attest-scan.dsse.json
attest-package.dsse.json
attest-push.dsse.json
```
Include the SHA-256 digest of each envelope in promotion manifests (`docs/release/promotion-attestations.md`) so downstream verifiers can trace chain of custody.
---
## 7. References
- [In-toto Statement v1](https://in-toto.io/spec/v1)
- [DSSE specification](https://github.com/secure-systems-lab/dsse)
- `docs/modules/signer/architecture.md`
- `docs/modules/attestor/architecture.md`
- `docs/release/promotion-attestations.md`
Keep this file updated alongside `DSSE-LIB-401-020` and `DSSE-CLI-401-021`. When the bench repo publishes sample attestations, link them here.