carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1// Package hostquarantine is the deployment-wide admission gate for enrolled
2// boxes. The authoritative directory implementation is deliberately outside
3// this package; callers supply an exact-object reader and every Check performs
4// a fresh lookup.
5package hostquarantine
6
7import (
8 "context"
9 "errors"
10 "fmt"
11 "regexp"
12 "strconv"
13 "strings"
14 "time"
15)
16
17const (
18 StateActive = "active"
19 StateQuarantined = "quarantined"
20 StateRetired = "retired"
21)
22
23var (
24 ErrAdmission = errors.New("hostquarantine: admission refused")
25 ErrRecord = errors.New("hostquarantine: malformed retirement completion")
26)
27
28type Actor struct {
29 Enrollment string `json:"enrollment"`
30 State string `json:"state"`
31 QuarantineRevision uint64 `json:"quarantine_revision"`
32}
33
34// ActorDirectory reads one exact actor from the root-signed deployment
35// directory. Implementations must return an error for missing, unreadable or
36// unverifiable authority.
37type ActorDirectory interface {
38 Lookup(context.Context, string) (Actor, error)
39}
40
41// Check admits only the exact active enrollment returned by a fresh lookup.
42// Missing actors, unreadable authority, identity mismatches, quarantine,
43// retirement and unknown states all fail closed.
44func Check(ctx context.Context, dir ActorDirectory, box, enrollment string) error {
45 if dir == nil || box == "" || enrollment == "" {
46 return fmt.Errorf("%w: actor identity or directory is absent", ErrAdmission)
47 }
48 a, err := dir.Lookup(ctx, box)
49 if err != nil {
50 return fmt.Errorf("%w: actor directory lookup: %v", ErrAdmission, err)
51 }
52 if a.Enrollment == "" || a.Enrollment != enrollment {
53 return fmt.Errorf("%w: enrollment mismatch", ErrAdmission)
54 }
55 if a.State != StateActive {
56 return fmt.Errorf("%w: actor state %q", ErrAdmission, a.State)
57 }
58 return nil
59}
60
61type RetirementEntry struct {
62 Host string `json:"host"`
63 Incarnation string `json:"incarnation"`
64 Stamp uint64 `json:"stamp"`
65 RetirementID string `json:"retirement_id"`
66 JournalSHA256 string `json:"journal_sha256"`
67}
68
69// RetirementCompletion is the returning machine's durable retirement
70// receipt. Readmission also requires the matching fresh private box report;
71// this object alone does not grant admission.
72type RetirementCompletion struct {
73 V int `json:"v"`
74 Box string `json:"box"`
75 Enrollment string `json:"enrollment"`
76 InstanceID string `json:"instance_id"`
77 QuarantineRevision uint64 `json:"quarantine_revision"`
78 BootID string `json:"boot_id"`
79 CompletedAt time.Time `json:"completed_at"`
80 Complete bool `json:"complete"`
81 InventorySHA256 string `json:"inventory_sha256"`
82 Entries []RetirementEntry `json:"entries"`
83}
84
85var sha256RE = regexp.MustCompile(`^[0-9a-f]{64}$`)
86
87func safeSegment(s string) bool {
88 return s != "" && s != "." && s != ".." && !strings.ContainsAny(s, "/\\")
89}
90
91func RetirementKey(box, enrollment string, revision uint64) (string, error) {
92 if !safeSegment(box) || !safeSegment(enrollment) || revision == 0 {
93 return "", fmt.Errorf("%w: invalid key identity", ErrRecord)
94 }
95 return "control/host-retirement/" + box + "/" + enrollment + "/" + strconv.FormatUint(revision, 10) + ".json", nil
96}
97
98func (r RetirementCompletion) Validate() error {
99 if r.V != 1 || !safeSegment(r.Box) || !safeSegment(r.Enrollment) || r.InstanceID == "" ||
100 r.QuarantineRevision == 0 || r.BootID == "" {
101 return fmt.Errorf("%w: invalid header", ErrRecord)
102 }
103 if r.Complete {
104 if r.CompletedAt.IsZero() || !sha256RE.MatchString(r.InventorySHA256) || r.Entries == nil {
105 return fmt.Errorf("%w: completed receipt lacks durable inventory evidence", ErrRecord)
106 }
107 } else if !r.CompletedAt.IsZero() {
108 return fmt.Errorf("%w: incomplete receipt has completed_at", ErrRecord)
109 }
110 for i, e := range r.Entries {
111 if e.Host == "" || e.Incarnation == "" || e.Stamp == 0 || e.RetirementID == "" || !sha256RE.MatchString(e.JournalSHA256) {
112 return fmt.Errorf("%w: invalid entry %d", ErrRecord, i)
113 }
114 }
115 return nil
116}
117