carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package hostactors
2
3import (
4 "context"
5 "crypto/rand"
6 "encoding/hex"
7 "github.com/carlosframework/platform/internal/boxid"
8 "github.com/carlosframework/platform/internal/hostfloor"
9 "github.com/carlosframework/platform/internal/release"
10 "time"
11)
12
13type Region struct {
14 Namespaces []string `json:"namespaces"`
15 Pools []string `json:"pools"`
16 Account string `json:"account"`
17 AWSRegion string `json:"aws_region"`
18 GroupName string `json:"group_name"`
19 GroupARN string `json:"group_arn"`
20 // Role is the role every box automatic enrollment admits from the
21 // group takes (roles.go): "" (default) or legacy.
22 Role string `json:"role,omitempty"`
23}
24
25// EnrollAutomatic admits a box its region's group launched (H16, H28). The
26// console confirms the box's group membership itself, here: Member is
27// asked, through r, and every store call of the enrolling write,
28// checkMembership's reads and Replace's Put included, runs on the store
29// writeBound binds to that read's deadline, the single enforcement point:
30// none is issued at or after it. No box writes or contributes membership
31// evidence, and no caller can hand a confirmation in: membershipread.go
32// builds it from r's answer alone. The evidence comes back whenever the
33// cloud answered, so every read can be logged.
34func (d Directory) EnrollAutomatic(ctx context.Context, sg release.Signer, o Observed, v boxid.Verifier, in EnrollmentRequest, raw []byte, instance string, r MembershipReader, now func() time.Time) (Observed, MembershipEvidence, error) {
35 pinned, c, err := d.checkConfirmed(ctx, o, v, in, raw, instance, r, now)
36 if err != nil {
37 return Observed{}, c.evidence(), err
38 }
39 bd, bctx, cancel := writeBound(ctx, d, c, now)
40 defer cancel()
41 next, err := bd.enroll(bctx, sg, o, v, pinned, raw, true)
42 if err != nil {
43 return Observed{}, c.evidence(), err
44 }
45 // Callers go on with the store itself, not the bound one.
46 return next, c.evidence(), nil
47}
48
49// checkConfirmed is EnrollAutomatic's admission check, made without
50// writing: the attempt in the roster matches the request and the record,
51// the request's reference is the instance's group reference
52// (GroupBoxReference), and a describe made now confirms the attempt's
53// instance is a current member of the region's group. It returns in
54// pinned to that instance and the confirmation the read built, which
55// never leaves this package. The console's enrollment pass asks it
56// immediately before it invites a box, and makes the invitation on the
57// store writeBound binds to that confirmation (autoenroll.go).
58func (d Directory) checkConfirmed(ctx context.Context, o Observed, v boxid.Verifier, in EnrollmentRequest, raw []byte, instance string, r MembershipReader, now func() time.Time) (EnrollmentRequest, membershipConfirmation, error) {
59 if err := validateScope(o.Roster, Scope{Namespace: in.Namespace, Regions: []string{in.Region}, Pools: []string{in.Pool}}); err != nil {
60 return EnrollmentRequest{}, membershipConfirmation{}, err
61 }
62 region, ok := o.Roster.Regions[in.Region]
63 if !ok || region.Account != v.Account || region.AWSRegion != in.AWSRegion || region.GroupARN == "" || region.GroupName == "" || !segment(instance) || !segment(in.Reference) || in.ExistingReference {
64 return EnrollmentRequest{}, membershipConfirmation{}, ErrRoster
65 }
66 // A group box is enrolled only under the reference derived from its
67 // instance, never one it chose (census ruling region-ha-2b).
68 if in.Reference != GroupBoxReference(region.Account, instance) {
69 return EnrollmentRequest{}, membershipConfirmation{}, ErrRoster
70 }
71 attempt, ok := o.Roster.EnrollmentAttempts[in.Reference]
72 if !ok || attempt.Consumed || !token.MatchString(in.AttemptID) || !token.MatchString(in.ConfirmationID) || in.AttemptID == in.ConfirmationID || attempt.AttemptID != in.AttemptID || attempt.ConfirmationID != in.ConfirmationID || attempt.InstanceID != instance || attempt.EvidenceSHA256 != hostfloor.Digest(raw) || attempt.Region != in.Region || attempt.Account != region.Account || attempt.AWSRegion != region.AWSRegion || attempt.GroupARN != region.GroupARN || attempt.GroupName != region.GroupName {
73 return EnrollmentRequest{}, membershipConfirmation{}, ErrRoster
74 }
75 c, err := confirmMembership(ctx, r, region, instance, now)
76 if err != nil {
77 return EnrollmentRequest{}, c, err
78 }
79 if err := c.fresh(now()); err != nil {
80 return EnrollmentRequest{}, c, err
81 }
82 // Pin the verified IMDS identity to the instance that membership proved.
83 in.TofuInstanceID = instance
84 in.ExistingReference = true
85 return in, c, nil
86}
87
88type EnrollmentAttempt struct {
89 AttemptID string `json:"attempt_id"`
90 ConfirmationID string `json:"confirmation_id"`
91 InstanceID string `json:"instance_id"`
92 EvidenceSHA256 string `json:"evidence_sha256"`
93 Region string `json:"region"`
94 Account string `json:"account"`
95 AWSRegion string `json:"aws_region"`
96 GroupName string `json:"group_name"`
97 GroupARN string `json:"group_arn"`
98 Consumed bool `json:"consumed"`
99}
100
101// BeginEnrollmentAttempt supersedes evidence by CAS in the signed authority.
102// A retry after expiry obtains new IDs; old immutable confirmations remain.
103func (d Directory) BeginEnrollmentAttempt(ctx context.Context, sg release.Signer, o Observed, in EnrollmentRequest, evidence []byte, instance string) (Observed, EnrollmentAttempt, error) {
104 region, ok := o.Roster.Regions[in.Region]
105 if !ok || !segment(in.Reference) || !segment(instance) || region.Account == "" || region.AWSRegion == "" || region.GroupName == "" || region.GroupARN == "" {
106 return Observed{}, EnrollmentAttempt{}, ErrRoster
107 }
108 if _, exists := o.Roster.Boxes[in.Reference]; exists {
109 return Observed{}, EnrollmentAttempt{}, ErrRoster
110 }
111 var nonce [32]byte
112 if _, err := rand.Read(nonce[:]); err != nil {
113 return Observed{}, EnrollmentAttempt{}, err
114 }
115 a := EnrollmentAttempt{AttemptID: hex.EncodeToString(nonce[:16]), ConfirmationID: hex.EncodeToString(nonce[16:]), InstanceID: instance, EvidenceSHA256: hostfloor.Digest(evidence), Region: in.Region, Account: region.Account, AWSRegion: region.AWSRegion, GroupName: region.GroupName, GroupARN: region.GroupARN}
116 if a.AttemptID == a.ConfirmationID {
117 return Observed{}, EnrollmentAttempt{}, ErrRoster
118 }
119 next := Clone(o.Roster)
120 if next.EnrollmentAttempts == nil {
121 next.EnrollmentAttempts = map[string]EnrollmentAttempt{}
122 }
123 next.EnrollmentAttempts[in.Reference] = a
124 updated, err := d.Replace(ctx, sg, o, next)
125 return updated, a, err
126}
127