package hostactors import ( "context" "crypto" "crypto/rand" "crypto/rsa" "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/json" "github.com/carlosframework/platform/internal/boxid" "testing" "time" ) func TestEnrollmentRejectsSupersededAndConsumedAttempts(t *testing.T) { ctx := context.Background() d, sg, o := fixture(t) key, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { t.Fatal(err) } doc := []byte(`{"accountId":"123","region":"eu-west-1","instanceId":"i-new"}`) sum := sha256.Sum256(doc) sig, err := rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, sum[:]) if err != nil { t.Fatal(err) } rec := boxid.Record{V: 1, Box: newRef, IdentityDocument: base64.StdEncoding.EncodeToString(doc), IdentitySignature: base64.StdEncoding.EncodeToString(sig)} raw, _ := json.Marshal(rec) v := boxid.Verifier{Account: "123", Certs: map[string]*x509.Certificate{"eu-west-1": {PublicKey: &key.PublicKey, PublicKeyAlgorithm: x509.RSA}}} in := EnrollmentRequest{Reference: newRef, Namespace: "platform", Label: newRef, Region: "ie", Pool: "default", AWSRegion: "eu-west-1"} next := Clone(o.Roster) next.Regions = map[string]Region{"ie": {Account: "123", AWSRegion: "eu-west-1", GroupName: "routers", GroupARN: "group-arn", Namespaces: []string{"platform"}, Pools: []string{"default"}}} o, err = d.Replace(ctx, sg, o, next) if err != nil { t.Fatal(err) } if _, err = d.Invite(ctx, sg, in.Reference, raw); err != nil { t.Fatal(err) } o, first, err := d.BeginEnrollmentAttempt(ctx, sg, o, in, raw, "i-new") if err != nil { t.Fatal(err) } // The console's own read, which says the instance is a member. cloud := &fakeMembership{members: map[string]Membership{"i-new": member("routers")}} now := func() time.Time { return time.Now().UTC() } staleRoster := o o, second, err := d.BeginEnrollmentAttempt(ctx, sg, o, in, raw, "i-new") if err != nil { t.Fatal(err) } if first.AttemptID == second.AttemptID || first.ConfirmationID == second.ConfirmationID { t.Fatal("immutable identity reused") } in.AttemptID, in.ConfirmationID = first.AttemptID, first.ConfirmationID if _, _, err = d.EnrollAutomatic(ctx, sg, o, v, in, raw, "i-new", cloud, now); err == nil { t.Fatal("superseded request admitted") } // The superseded roster snapshot loses its CAS even with its own // attempt's ids. if _, _, err = d.EnrollAutomatic(ctx, sg, staleRoster, v, in, raw, "i-new", cloud, now); err == nil { t.Fatal("superseded snapshot admitted") } in.AttemptID, in.ConfirmationID = second.AttemptID, second.ConfirmationID admitted, _, err := d.EnrollAutomatic(ctx, sg, o, v, in, raw, "i-new", cloud, now) if err != nil { t.Fatal(err) } if !admitted.Roster.EnrollmentAttempts[in.Reference].Consumed { t.Fatal("attempt not consumed with roster admission") } if _, _, err = d.EnrollAutomatic(ctx, sg, admitted, v, in, raw, "i-new", cloud, now); err == nil { t.Fatal("consumed attempt replayed") } }