Commit 9772fc9
Eric Bower
·
2026-05-08 15:14:26 -0400 EDT
parent ab5ea95
feat: attestation
1 files changed,
+53,
-4
M
main.go
M
main.go
+53,
-4
| ... | ... | @@ -17,6 +17,7 @@ import ( | |
| 17 | 17 | "os/exec" | |
| 18 | 18 | "os/signal" | |
| 19 | 19 | "path/filepath" | |
| 20 | + | "runtime" | |
| 20 | 21 | "strconv" | |
| 21 | 22 | "strings" | |
| 22 | 23 | "syscall" |
| ... | ... | @@ -313,6 +314,9 @@ type Workspace interface { | |
| 313 | 314 | Setup() error | |
| 314 | 315 | Cleanup() error | |
| 315 | 316 | GetDir() string | |
| 317 | + | // Checksum returns a content hash of the workspace (e.g. sha256 of the tarball). | |
| 318 | + | // Returns empty string if not available. | |
| 319 | + | Checksum() string | |
| 316 | 320 | } | |
| 317 | 321 | ||
| 318 | 322 | type WorkspaceRsync struct { |
| ... | ... | @@ -355,11 +359,16 @@ func (w *WorkspaceRsync) GetDir() string { | |
| 355 | 359 | return w.Dest | |
| 356 | 360 | } | |
| 357 | 361 | ||
| 362 | + | func (w *WorkspaceRsync) Checksum() string { | |
| 363 | + | return "" | |
| 364 | + | } | |
| 365 | + | ||
| 358 | 366 | type WorkspaceTar struct { | |
| 359 | - | Cfg *Cfg | |
| 360 | - | Logger *slog.Logger | |
| 361 | - | Source string // e.g. "pgs.sh:/private-ci/workspaces/repo_abc123.tar" | |
| 362 | - | Dest string | |
| 367 | + | Cfg *Cfg | |
| 368 | + | Logger *slog.Logger | |
| 369 | + | Source string // e.g. "pgs.sh:/private-ci/workspaces/repo_abc123.tar" | |
| 370 | + | Dest string | |
| 371 | + | checksum string | |
| 363 | 372 | } | |
| 364 | 373 | ||
| 365 | 374 | func (w *WorkspaceTar) Setup() error { |
| ... | ... | @@ -383,6 +392,14 @@ func (w *WorkspaceTar) Setup() error { | |
| 383 | 392 | return fmt.Errorf("rsync download: %w", err) | |
| 384 | 393 | } | |
| 385 | 394 | ||
| 395 | + | // Compute sha256 checksum of the tarball | |
| 396 | + | tarData, err := os.ReadFile(tarPath) | |
| 397 | + | if err != nil { | |
| 398 | + | return fmt.Errorf("read tar for checksum: %w", err) | |
| 399 | + | } | |
| 400 | + | w.checksum = fmt.Sprintf("sha256:%x", sha256.Sum256(tarData)) | |
| 401 | + | log.Debug("workspace checksum", "checksum", w.checksum) | |
| 402 | + | ||
| 386 | 403 | // Open the tar file for extraction | |
| 387 | 404 | f, err := os.Open(tarPath) | |
| 388 | 405 | if err != nil { |
| ... | ... | @@ -436,6 +453,10 @@ func (w *WorkspaceTar) Setup() error { | |
| 436 | 453 | return nil | |
| 437 | 454 | } | |
| 438 | 455 | ||
| 456 | + | func (w *WorkspaceTar) Checksum() string { | |
| 457 | + | return w.checksum | |
| 458 | + | } | |
| 459 | + | ||
| 439 | 460 | func (w *WorkspaceTar) Cleanup() error { | |
| 440 | 461 | return nil | |
| 441 | 462 | } |
| ... | ... | @@ -586,6 +607,26 @@ func eventHandler(cfg *Cfg, eventData *Event) error { | |
| 586 | 607 | } | |
| 587 | 608 | } | |
| 588 | 609 | ||
| 610 | + | // Write attestation.json with runner/workspace provenance | |
| 611 | + | hostname, _ := os.Hostname() | |
| 612 | + | attestation := map[string]interface{}{ | |
| 613 | + | "runner": map[string]string{ | |
| 614 | + | "hostname": hostname, | |
| 615 | + | "os": runtimeOS(), | |
| 616 | + | "arch": runtimeArch(), | |
| 617 | + | }, | |
| 618 | + | "provenance": map[string]string{ | |
| 619 | + | "repo": eventData.Name, | |
| 620 | + | "branch": eventData.Branch, | |
| 621 | + | "commit": eventData.Commit, | |
| 622 | + | }, | |
| 623 | + | "workspace_checksum": eng.Wk.Checksum(), | |
| 624 | + | } | |
| 625 | + | attestationBytes, _ := json.Marshal(attestation) | |
| 626 | + | if err := os.WriteFile(filepath.Join(eventDir, "attestation.json"), attestationBytes, 0644); err != nil { | |
| 627 | + | log.Error("write attestation", "err", err) | |
| 628 | + | } | |
| 629 | + | ||
| 589 | 630 | manifest, err := eng.FindManifest() | |
| 590 | 631 | if err != nil { | |
| 591 | 632 | fmt.Fprintf(os.Stdout, "❌ %s\n\n", err) //nolint:errcheck |
| ... | ... | @@ -1293,6 +1334,14 @@ func generateJobID(name, workspace string) string { | |
| 1293 | 1334 | return jobIDFor(name, workspace, time.Now().UnixNano()) | |
| 1294 | 1335 | } | |
| 1295 | 1336 | ||
| 1337 | + | func runtimeOS() string { | |
| 1338 | + | return runtime.GOOS | |
| 1339 | + | } | |
| 1340 | + | ||
| 1341 | + | func runtimeArch() string { | |
| 1342 | + | return runtime.GOARCH | |
| 1343 | + | } | |
| 1344 | + | ||
| 1296 | 1345 | func jobIDFor(name, workspace string, ts int64) string { | |
| 1297 | 1346 | h := sha256.Sum256([]byte(name + workspace + fmt.Sprintf("%d", ts))) | |
| 1298 | 1347 | return fmt.Sprintf("%x", h[:4]) |