guildhouse-spire-plugins/cmd/gsap-attestor/proc.go
Tyler J King fe5e2cf3c6 feat(spire): gsap-attestor WorkloadAttestor plugin
SPIRE WorkloadAttestor that reads governance env vars from /proc/{pid}/environ
(walking up the process tree to find gsh) and emits gsap: selectors on workload
SVIDs. Maps BASCULE_* vars set by bascule-shell and future GSH_* vars to the
11-selector vocabulary defined in gsap-types/src/selectors.rs.

- pkg/gsap/selectors.go: shared Go constants mirroring Rust vocabulary
- cmd/gsap-attestor/: plugin implementation with /proc reading, process tree
  walking, capability ceiling translation, and fail-open for non-governed processes
- 28 tests covering selector extraction, proc parsing, tree walking, and depth limits
- Makefile, Dockerfile, deploy config updated

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-13 03:59:08 -04:00

55 lines
1.3 KiB
Go

package main
import (
"bytes"
"fmt"
"os"
"strconv"
"strings"
)
// readProcEnviron reads /proc/{pid}/environ (NUL-delimited KEY=VALUE pairs)
// and returns the environment as a map.
func readProcEnviron(procRoot string, pid int32) (map[string]string, error) {
path := fmt.Sprintf("%s/%d/environ", procRoot, pid)
data, err := os.ReadFile(path)
if err != nil {
return nil, err
}
env := make(map[string]string)
for _, entry := range bytes.Split(data, []byte{0}) {
if len(entry) == 0 {
continue
}
parts := bytes.SplitN(entry, []byte("="), 2)
if len(parts) == 2 {
env[string(parts[0])] = string(parts[1])
}
}
return env, nil
}
// getParentPid reads /proc/{pid}/status and extracts the PPid field.
// Returns 0 if pid 1 (init) is reached or the field cannot be parsed.
func getParentPid(procRoot string, pid int32) (int32, error) {
path := fmt.Sprintf("%s/%d/status", procRoot, pid)
data, err := os.ReadFile(path)
if err != nil {
return 0, err
}
for _, line := range strings.Split(string(data), "\n") {
if strings.HasPrefix(line, "PPid:") {
fields := strings.Fields(line)
if len(fields) >= 2 {
ppid, err := strconv.ParseInt(fields[1], 10, 32)
if err != nil {
return 0, fmt.Errorf("parse PPid in %s: %w", path, err)
}
return int32(ppid), nil
}
}
}
return 0, fmt.Errorf("PPid not found in %s", path)
}