package inventory import ( "context" "testing" "github.com/novox/mesh-controller/internal/catalogue" "github.com/novox/mesh-controller/internal/secrets" ) // With an operator key, a module's own secret is sealed to the operator as well — and the operator // opens exactly the value the node was given. func TestAnOwnSecretIsSealedToTheOperatorToo(t *testing.T) { inv, ctx := twoNodesWithKeys(t) if err := inv.RegisterModule(ctx, catalogue.Manifest{Module: "postgres", Version: "1"}, Source{}); err != nil { t.Fatal(err) } // Before there is a key: minted, and honestly unrecoverable. if _, err := inv.SecretForModule(ctx, "consumer", "postgres", "superuser"); err != nil { t.Fatal(err) } if _, err := inv.KeptSecret(ctx, "consumer", "postgres", "superuser"); err == nil { t.Fatal("a secret made before the operator key was reported recoverable") } kept, unrecoverable, err := inv.KeptForOperator(ctx) if err != nil { t.Fatal(err) } if len(kept) != 0 || len(unrecoverable) != 1 { t.Fatalf("before a key: %d kept, %d unrecoverable", len(kept), len(unrecoverable)) } pub, priv, err := secrets.Keypair() if err != nil { t.Fatal(err) } if orphaned, err := inv.SetOperatorKey(ctx, pub); err != nil || orphaned != 0 { t.Fatalf("set: orphaned %d, %v", orphaned, err) } // A new secret is sealed to both; an accepted one too. if _, err := inv.SecretForModule(ctx, "provider", "postgres", "superuser"); err != nil { t.Fatal(err) } if err := inv.AcceptSecretForModule(ctx, "provider", "postgres", "replication", "given-by-a-person"); err != nil { t.Fatal(err) } kept, unrecoverable, err = inv.KeptForOperator(ctx) if err != nil { t.Fatal(err) } if len(kept) != 2 || len(unrecoverable) != 1 { t.Fatalf("after a key: %d kept, %d unrecoverable", len(kept), len(unrecoverable)) } got, err := inv.KeptSecret(ctx, "provider", "postgres", "replication") if err != nil { t.Fatal(err) } value, err := secrets.Open(priv, got.Sealed) if err != nil { t.Fatal(err) } if string(value) != "given-by-a-person" { t.Fatalf("recovered %q", value) } if got.Origin != "accepted" || got.Key != pub { t.Fatalf("kept as %+v", got) } minted, err := inv.KeptSecret(ctx, "provider", "postgres", "superuser") if err != nil { t.Fatal(err) } if v, err := secrets.Open(priv, minted.Sealed); err != nil || len(v) != 40 { t.Fatalf("the minted secret did not open to a 40-character value: %v", err) } // The old secret, remade for a rejoined node, becomes recoverable — it was issued again. if _, err := inv.SecretForModule(ctx, "consumer", "postgres", "superuser"); err != nil { t.Fatal(err) } if _, err := inv.KeptSecret(ctx, "consumer", "postgres", "superuser"); err == nil { t.Fatal("asking again did not remake, yet it became recoverable") } // Replacing the key says how many secrets stay sealed to the old one. pub2, _, _ := secrets.Keypair() orphaned, err := inv.SetOperatorKey(ctx, pub2) if err != nil { t.Fatal(err) } if orphaned != 2 { t.Fatalf("replacing the key orphaned %d, and two were sealed to it", orphaned) } if now, _ := inv.OperatorKey(ctx); now != pub2 { t.Fatal("the new key is not the mesh's key") } } // A pair credential — what the vault provides a module — is sealed to the operator too, and the // operator's copy is the very value the consumer's node unseals. func TestAPairCredentialIsSealedToTheOperatorToo(t *testing.T) { inv := fresh(t) ctx := context.Background() // Two nodes with keys, keeping the consumer's opener: the test made the key, so it can play the // consumer's host for one assertion. var openAsConsumer func(string) ([]byte, bool) for _, n := range []string{"consumer", "provider"} { node, err := inv.AddNode(ctx, n) if err != nil { t.Fatal(err) } key, open := aSealingKey(t) if n == "consumer" { openAsConsumer = open } if err := inv.RecordSealingKey(ctx, node.ID, key); err != nil { t.Fatal(err) } } for _, m := range []string{"gitea", "mesh-vault"} { if err := inv.RegisterModule(ctx, catalogue.Manifest{Module: m, Version: "1"}, Source{}); err != nil { t.Fatal(err) } } pub, priv, err := secrets.Keypair() if err != nil { t.Fatal(err) } if _, err := inv.SetOperatorKey(ctx, pub); err != nil { t.Fatal(err) } made, err := inv.SecretFor(ctx, "secret", "consumer", "gitea", "provider") if err != nil { t.Fatal(err) } kept, err := inv.KeptSecret(ctx, "consumer", "gitea", "secret") if err != nil { t.Fatal(err) } if kept.Kind != "pair" || kept.Provider != "provider" { t.Fatalf("kept as %+v", kept) } fromOperator, err := secrets.Open(priv, kept.Sealed) if err != nil { t.Fatal(err) } // The consumer's blob is sealed to the consumer node. Same value, two recipients. fromNode, ok := openAsConsumer(made.ForConsumer) if !ok { t.Fatal("the consumer cannot open its own blob") } if string(fromOperator) != string(fromNode) { t.Fatal("the operator's copy of the pair credential differs from the consumer's") } all, _, err := inv.KeptForOperator(ctx) if err != nil { t.Fatal(err) } var pairs int for _, k := range all { if k.Kind == "pair" { pairs++ } } if pairs != 1 { t.Fatalf("%d pair credential(s) in the export, expected 1", pairs) } }