Land feature 57's controller work on its release branch
The controller plans config, templates and moved providers' provisions, hands bus accounts to the vault and judges the new manifest words, on the feature's SDK and node-engine validator (feature 57, config and provisioning).
This commit is contained in:
+128
-4
@@ -150,6 +150,10 @@ func planFor(ctx context.Context, open *stores, nodeName string) (catalogue.Reso
|
||||
// password a provider is told to create is the one its consumer was given — and sealed to
|
||||
// this node before it was ever written down, so nothing between here and there can read it.
|
||||
for i, n := range resolved.Needs {
|
||||
// **Issued for this holder alone and kept by the vault** (issue 757): where this consumer is handed to the
|
||||
// provisioner, its machine's client asks the vault for it, and the declaration carries no value.
|
||||
resolved.Needs[i].IssuedToHolder = issuedToHolder(shelf, n)
|
||||
resolved.Needs[i].ProviderMoved = providerMoved(shelf, n)
|
||||
if n.ByRecord {
|
||||
// Answered by something the mesh holds, so there is no pair-wise secret between two
|
||||
// machines. Its key was supplied by a person and sealed to this node then; the mesh
|
||||
@@ -476,9 +480,23 @@ func declarationWith(ctx context.Context, open *stores, node string,
|
||||
out := sendable{Resources: composed.Resources, Adoption: adoption,
|
||||
Received: composed.Received, Mesh: with.Mesh, BusUsers: with.BusUsers,
|
||||
LeftOut: sortedKeysOf(composed.LeftOut), leftOutWhy: composed.LeftOut, withheld: with.Withheld,
|
||||
unbound: with.Unbound, foreseen: composed.Foreseen, unplaced: composed.Unplaced,
|
||||
modules: namedModules(plan, composed.LeftOut),
|
||||
unbound: with.Unbound, foreseen: composed.Foreseen, unplaced: composed.Unplaced, handedOnly: composed.HandedOnly,
|
||||
modules: namedModules(plan, composed.LeftOut),
|
||||
Filled: composed.Filled, plans: composed.Plans,
|
||||
localNetwork: catalogue.LocalNetworkPorts(rules, with.LocalNetworks)}
|
||||
if len(composed.Filled) > 0 {
|
||||
out.Provisioner = with.Provisioner
|
||||
}
|
||||
// A handed module only pulled in by another keeps what requires it, for its value of its own in the assignments
|
||||
// mirror (issue 765).
|
||||
for module := range composed.Plans {
|
||||
if by := plan.RequiredBy(module); by != "" {
|
||||
if out.requiredBy == nil {
|
||||
out.requiredBy = map[string]string{}
|
||||
}
|
||||
out.requiredBy[module] = by
|
||||
}
|
||||
}
|
||||
// And which build of each module it carries, for the send to record (novox/hq issue 259, ADR
|
||||
// 0221). Read only on the send path: a question about what would be sent records nothing.
|
||||
if choosing == Allocating {
|
||||
@@ -591,6 +609,10 @@ func reportLeftOut(node string, declared sendable) {
|
||||
for _, u := range declared.unplaced {
|
||||
fmt.Printf("%s: %s\n", node, u)
|
||||
}
|
||||
// And each file only the provisioner's client fills, left out because the module is not handed here.
|
||||
for _, u := range declared.handedOnly {
|
||||
fmt.Printf("%s: %s\n", node, u)
|
||||
}
|
||||
}
|
||||
|
||||
// busCredentialIssued refuses an own secret called `broker` whose bus account nobody issued.
|
||||
@@ -601,10 +623,17 @@ func reportLeftOut(node string, declared sendable) {
|
||||
// cannot read and report the machine applied. Refused by name, with the verb — on the send, and on
|
||||
// a question asked ahead of it (Foreseeing), so that one is never told the push will make it.
|
||||
func busCredentialIssued(ctx context.Context, inv *inventory.Inventory, node, module, name string) error {
|
||||
if name != "broker" {
|
||||
var user string
|
||||
switch name {
|
||||
case "broker":
|
||||
user = broker.Principal{Kind: broker.KindModule, Node: node, Module: module}.Username()
|
||||
case broker.ProvisionerSecret:
|
||||
// And the machine's provisioner client's account, issued with its module (novox/hq ADR 0349 clause 11,
|
||||
// design 57 P3): the same refusal, for the same reason.
|
||||
user = broker.Principal{Kind: broker.KindProvisioner, Node: node, Module: module}.Username()
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
user := broker.Principal{Kind: broker.KindModule, Node: node, Module: module}.Username()
|
||||
if _, minted, err := inv.BusUserHash(ctx, user); err != nil {
|
||||
return err
|
||||
} else if !minted {
|
||||
@@ -728,7 +757,18 @@ func renderingFor(ctx context.Context, open *stores, node string,
|
||||
// per node, so a module running on three machines has three.
|
||||
needed := map[string]map[string]string{}
|
||||
foreseen := map[string]map[string]bool{}
|
||||
// **A module handed to the provisioner here has its secrets from the vault** (novox/hq ADR 0349 §7, §11,
|
||||
// design 57 V4): its machine's client asks for each, so the controller makes, reads and seals none of them for
|
||||
// this machine. Every other module's are made and sealed as before.
|
||||
hands, err := engineTakesHanded(ctx, inv, node)
|
||||
if err != nil {
|
||||
return catalogue.Rendering{}, inventory.Node{}, err
|
||||
}
|
||||
handing := catalogue.Rendering{Hands: hands, Provisioner: provisionerAccount(plan)}
|
||||
for _, m := range plan.Modules {
|
||||
if handing.Handing(m) {
|
||||
continue
|
||||
}
|
||||
// **A secret family is never made** (novox/hq ADR 0283): each member a person gave on this machine is
|
||||
// placed, and one not given is nothing — the module says it waits for it.
|
||||
for _, family := range m.OwnSecrets.Families() {
|
||||
@@ -822,6 +862,22 @@ func renderingFor(ctx context.Context, open *stores, node string,
|
||||
needed[m.Module][name] = sealed
|
||||
}
|
||||
}
|
||||
// **The core's own credentials as the vault sealed them** (novox/hq ADR 0349 §16, issue 755), for an engine that
|
||||
// opens the vault's box (core_box.go): the declaration carries the box, never the seal made above. Below that
|
||||
// contract nothing here is asked and the composition is as it was.
|
||||
var core coreBoxes
|
||||
opensBox, err := engineOpensVaultBox(ctx, inv, node)
|
||||
if err != nil {
|
||||
return catalogue.Rendering{}, inventory.Node{}, err
|
||||
}
|
||||
if opensBox {
|
||||
core, err = coreBoxesFor(ctx, inv, node, plan.Modules, func(m catalogue.Manifest) bool {
|
||||
return handing.Handing(m)
|
||||
}, choosing)
|
||||
if err != nil {
|
||||
return catalogue.Rendering{}, inventory.Node{}, err
|
||||
}
|
||||
}
|
||||
// And a certificate for this machine's name inside the mesh, when anything on it asks. Issued
|
||||
// rather than stored: the node's key does not change, so signing again produces an equally
|
||||
// valid certificate and there is nothing to keep in step.
|
||||
@@ -1016,8 +1072,11 @@ func renderingFor(ctx context.Context, open *stores, node string,
|
||||
ReadsHealth: readsHealth,
|
||||
JudgesRoot: judgesRoot,
|
||||
ReadsBusy: readsBusy,
|
||||
Hands: hands,
|
||||
Provisioner: provisionerAccount(plan),
|
||||
BusMembership: memberships[node],
|
||||
Settings: settings, Generators: gens, Grants: grants, Needed: needed, Foreseen: foreseen, Ports: ports,
|
||||
VaultSealed: core.sealed, NotPlaced: core.notPlaced,
|
||||
Certificate: certificate, Authority: authority, Mesh: private, Names: names,
|
||||
Machines: machines, Zones: zones, Holders: replicas,
|
||||
Suffix: overlay.Suffix(), MeshRange: meshRange, TunnelInterface: overlay.Interface, Accounts: accounts, Foundation: foundation,
|
||||
@@ -1059,6 +1118,35 @@ func engineReadsBusy(ctx context.Context, inv *inventory.Inventory, node string)
|
||||
return had && stated.Contract >= link.BusyContract, nil
|
||||
}
|
||||
|
||||
// engineTakesHanded says whether a machine's node-engine writes a filled file on the provisioner client's word alone
|
||||
// (link.HandedContract, design 57 P7), by its own newest statement: only there is a moved module handed to the
|
||||
// provisioner (design 57 P6). Anywhere else a filled file would have two writers, and the module is sent as before.
|
||||
func engineTakesHanded(ctx context.Context, inv *inventory.Inventory, node string) (bool, error) {
|
||||
stated, had, err := inv.HealthOf(ctx, node)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return had && stated.Contract >= link.HandedContract, nil
|
||||
}
|
||||
|
||||
// provisionerAccount is the account the provisioner's client runs as on this machine (novox/hq ADR 0349 §4): the user
|
||||
// the process of the module running it runs as, empty where no such module is on the machine.
|
||||
func provisionerAccount(plan catalogue.Resolution) string {
|
||||
for _, m := range plan.Modules {
|
||||
if !runsTheProvisionerClient(m) {
|
||||
continue
|
||||
}
|
||||
for _, r := range m.Resources {
|
||||
if r["type"] == "process" {
|
||||
if user, _ := r["user"].(string); user != "" && user != "root" {
|
||||
return user
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// busyNotSent is, for a node-engine that does not read a container's busy, each container whose module declares
|
||||
// one: not sent it, so replaced at once while it may hold work (novox/hq ADR 0335). Nothing for one that reads it.
|
||||
func busyNotSent(modules []catalogue.Manifest, reads bool) []string {
|
||||
@@ -1846,6 +1934,42 @@ func issuedOutsideBy(shelf map[string]catalogue.Manifest, n catalogue.Needed) (s
|
||||
return catalogue.OffersIssuedOutside(shelf, n.Name)
|
||||
}
|
||||
|
||||
// issuedToHolder is whether the vault keeps a need's credential for its holder alone (issues 749, 757 and 779): its
|
||||
// providing module is marked moved, so the vault holds the credential in a row of its own for each holder
|
||||
// (filledTheNewWay), and its offer says either that the provider's software issues the provision's credential, or
|
||||
// that the provider gives each consumer a login of its own (form pair, ADR 0158 §11). A credential every holder
|
||||
// shares, or one a person gives, is neither.
|
||||
func issuedToHolder(shelf map[string]catalogue.Manifest, n catalogue.Needed) bool {
|
||||
if n.ByRecord || n.Module == "" {
|
||||
return false
|
||||
}
|
||||
provider, known := shelf[n.Module]
|
||||
if !known || !movedProvider(provider) {
|
||||
return false
|
||||
}
|
||||
if _, issued := provider.IssuedCredentialOf(n.Name); issued {
|
||||
return true
|
||||
}
|
||||
return provider.CredentialFormOf(n.Name) == catalogue.CredentialFormPair
|
||||
}
|
||||
|
||||
// providerMoved is whether a need is answered by a module marked moved (issue 788), or by one whose offer of the
|
||||
// provision says the provider helper answers it (issue 792).
|
||||
func providerMoved(shelf map[string]catalogue.Manifest, n catalogue.Needed) bool {
|
||||
if n.ByRecord || n.Module == "" {
|
||||
return false
|
||||
}
|
||||
provider, known := shelf[n.Module]
|
||||
return known && (movedProvider(provider) || provider.AnsweredByHelperFor(n.Name))
|
||||
}
|
||||
|
||||
// movedProvider is whether a provider's credentials are the vault's: the module is marked moved, or it holds the
|
||||
// mesh-vault seat, whose pair rows have been in the vault since V3 (issue 793), so its consumers' credentials
|
||||
// (gitea's internal token and admin password) are filled from those very rows with the values unchanged.
|
||||
func movedProvider(m catalogue.Manifest) bool {
|
||||
return m.Moved || slices.ContainsFunc(m.Claims, func(c catalogue.Claim) bool { return c.Name == "mesh-vault" })
|
||||
}
|
||||
|
||||
func providerModuleOf(resolved catalogue.Resolution, open *stores, ctx context.Context, n catalogue.Needed) string {
|
||||
for _, m := range resolved.Modules {
|
||||
if _, shared := m.SharedCredentialOf(n.Name); shared {
|
||||
|
||||
Reference in New Issue
Block a user