A consumer can write its own connection string
novox/hq 04-ISSUES/023. A consumer was given its password, the address,
the port and where its credential lives, and still could not connect —
the user name was invented by the provisioner and recorded nowhere, and
the rest sat in a JSON binding that a program reading KEY=value cannot
use.
Both halves have the same cause: the mesh knew something and did not say
it.
**Who a consumer is, said once.** The provisioner used to derive
mesh_<node>_<module> and that string existed nowhere else — not in the
control plane, not in the binding, and above all not at the consumer,
which has to present it. Now the mesh derives it once and sends it to
both ends, so they agree by construction rather than by two conventions
that were the same on the day they were written. The provisioners refuse
to invent one if the mesh says nothing, because falling back to a name
of their own would create a role the consumer would never guess and
everything would report success.
**Bound values reach the file that needs them.** ${bound:provision:key}
is the symmetric twin of the sealed placeholder, and simpler: these
values are not secret, so the control plane fills them in before sending
and the host gains no field and learns no format. It stays
name-agnostic — at, as and from are true of any provision, and every
other key comes from what the provider said it serves.
The asymmetry it removes was backwards. The secret is the hard case,
because the mesh must not be able to read it, and the secret was the
part that already arrived.
Keycloak and Gitea now produce complete connections, asserted from the
manifests on disk rather than from fixtures: every part filled, no
placeholder surviving as a value, and the password still a hole only the
host can close. Three faults injected, each caught.
This commit is contained in:
@@ -263,7 +263,7 @@ func (r Resolution) Declaration(with Rendering) ([]map[string]any, error) {
|
||||
}
|
||||
found = here
|
||||
}
|
||||
file, err := boundFile(*found, m.Binds[to])
|
||||
file, err := boundFile(*found, m.Binds[to], ConsumerIdentity(r.Node, m.Module))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -317,6 +317,8 @@ func (r Resolution) Declaration(with Rendering) ([]map[string]any, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// And what its bindings say, for the half of a connection that is not secret.
|
||||
known := knownFor(m, r.Needs, r.Node)
|
||||
|
||||
for _, unsettled := range resources {
|
||||
resource, err := ApplySettings(unsettled, with.Settings[m.Module])
|
||||
@@ -334,6 +336,12 @@ func (r Resolution) Declaration(with Rendering) ([]map[string]any, error) {
|
||||
if err := intoFile(copied, sealed, m.Module); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// **Substituted here, not on the machine.** A bound value is not secret — the mesh
|
||||
// holds it in the clear — so there is nothing for the host to be the only witness of,
|
||||
// and sending it already filled in means the host learns no new field.
|
||||
if err := boundInto(copied, known, m.Module); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copied["id"] = m.Module + "." + fmt.Sprint(resource["id"])
|
||||
// A service saying what it reflects names resources within its own module, so those
|
||||
// are prefixed too or they would point at nothing.
|
||||
@@ -382,6 +390,13 @@ type Contribution struct {
|
||||
// provider that reaches back to a consumer would have to know how the mesh names machines,
|
||||
// which is a convention leaking into every module that implements a provision.
|
||||
At string `json:"at,omitempty"`
|
||||
// As is who this consumer is: what the provider should call the login it creates.
|
||||
//
|
||||
// **Said by the mesh rather than invented by the provisioner** (novox/hq 04-ISSUES/023). It
|
||||
// used to be neither — the provisioner made a name, and the consumer, which has to present it
|
||||
// to authenticate, had no way to learn it. One derivation reaches both ends, so they agree by
|
||||
// construction.
|
||||
As string `json:"as"`
|
||||
// Secret is the file on this machine holding that consumer's credential, sealed to it.
|
||||
//
|
||||
// Named rather than carried, for the same reason the private network's key is: the mesh
|
||||
@@ -438,6 +453,7 @@ func (r Resolution) contributions(settings SettingsBy, grants []Grant,
|
||||
}
|
||||
out[g.Provision] = append(out[g.Provision], Contribution{
|
||||
From: g.From, Node: g.Consumer, At: g.At, Values: g.Values,
|
||||
As: ConsumerIdentity(g.Consumer, g.From),
|
||||
Secret: grantPath(directories[g.Provision], g.Consumer, g.From),
|
||||
})
|
||||
}
|
||||
@@ -499,7 +515,7 @@ func sortedKeys[V any](m map[string]V) []string {
|
||||
// Where it is and what the providing module said about using it. **No credential**, and the file
|
||||
// says so rather than leaving a reader to wonder whether one was meant to be there — a missing
|
||||
// field looks like a bug, and a stated absence looks like a boundary.
|
||||
func boundFile(n Needed, path string) (map[string]any, error) {
|
||||
func boundFile(n Needed, path, as string) (map[string]any, error) {
|
||||
// A record has no machine and no address. Saying so is the difference between a reader
|
||||
// concluding "somewhere with no address" and concluding the mesh failed to fill something in.
|
||||
where := any(n.At)
|
||||
@@ -511,7 +527,11 @@ func boundFile(n Needed, path string) (map[string]any, error) {
|
||||
"provision": n.Name,
|
||||
"from": n.From,
|
||||
"at": where,
|
||||
"serves": n.Serves,
|
||||
// Who this module is at the other end. **The half that was missing** (novox/hq
|
||||
// 04-ISSUES/023): a consumer was told the address, the port and where its password is,
|
||||
// and not the name it must present — which the provisioner had invented.
|
||||
"as": as,
|
||||
"serves": n.Serves,
|
||||
// **Where the credential is, not what it is.** It stopped being true that the mesh
|
||||
// cannot issue one when 021 was fixed, and a comment asserting a fact about the mesh that
|
||||
// has become false is worse than none — somebody reads it and stops looking.
|
||||
|
||||
Reference in New Issue
Block a user