Prove a hole in a config file is filled, on a real machine
Nothing did. The sealed-placeholder substitution and the bound-value
substitution were each covered by unit tests in the repository that
performs them, and the two expressions that find the holes live in
different repositories — so both sides could agree with themselves and
disagree with each other, and the first thing to notice would be a
program connecting to a host called "${bound:postgres-database:at}".
So the consumer in the credential test now ships a configuration file
with four holes in it: the address and port from what the provider
serves, the name to present from what the mesh decided, and the password
sealed. The mesh fills the first three before sending, the host opens
the credential and fills the last on the machine, and the test reads the
file off the machine and checks that the password in it is the same one
the credential file holds — and that no ${ survived.
This is the only place those two mechanisms meet a real host.
This commit is contained in:
@@ -248,10 +248,18 @@ test("a credential reaches both ends and the mesh holds neither", { skip, timeou
|
||||
`"provides":[{"name":"postgres-database","scope":"mesh"}],"serves":{"postgres-database":{"port":5432}},` +
|
||||
`"grants":{"postgres-database":"/var/lib/mesh-host/grants"},` +
|
||||
`"receives":{"postgres-database":"/var/lib/mesh-host/grants/mesh.json"},"resources":[]}' > /tmp/pg.json`);
|
||||
// The consumer also writes a configuration file with a hole in it, which is how nearly every
|
||||
// real program takes a credential: a sealed file is a password alone, and almost nothing reads
|
||||
// one. The mesh cannot compose the document — it discarded the value — so the module supplies it
|
||||
// with `${secret:...}` in it and the host, the only thing that sees both halves, fills it in.
|
||||
await must("anchor", `printf %s '{"module":"meshboard","version":"1",` +
|
||||
`"requires":["postgres-database"],"contributes":{"postgres-database":{"name":"meshboard"}},` +
|
||||
`"binds":{"postgres-database":"/etc/meshboard/database.json"},` +
|
||||
`"secrets":{"postgres-database":"/etc/meshboard/database.password"},"resources":[]}' > /tmp/app.json`);
|
||||
`"secrets":{"postgres-database":"/etc/meshboard/database.password"},` +
|
||||
`"resources":[{"id":"env","type":"file","path":"/etc/meshboard/database.env","mode":"0600",` +
|
||||
`"content":"PGHOST=$\{bound:postgres-database:at\}\\nPGPORT=$\{bound:postgres-database:port\}\\n` +
|
||||
`PGUSER=$\{bound:postgres-database:as\}\\nPGPASSWORD=$\{secret:postgres-database\}\\n"}]}' ` +
|
||||
`> /tmp/app.json`);
|
||||
await must("anchor", `docker cp /tmp/pg.json mesh-control:/pg.json`);
|
||||
await must("anchor", `docker cp /tmp/app.json mesh-control:/app.json`);
|
||||
await mesh("module add /pg.json");
|
||||
@@ -281,6 +289,24 @@ test("a credential reaches both ends and the mesh holds neither", { skip, timeou
|
||||
// Only the machine it is for may read it.
|
||||
assert.match(await must("laptop", `stat -c %a /etc/meshboard/database.password`), /^600/);
|
||||
|
||||
// And the configuration with holes in it arrived filled. **This is the only place the two
|
||||
// substitutions are proven against a real host**: the mesh fills what it knows in the clear
|
||||
// before sending, the host opens the sealed value and fills the rest on the machine, and the
|
||||
// two expressions that find the holes live in different repositories.
|
||||
const filled = await must("laptop", `cat /etc/meshboard/database.env`);
|
||||
assert.match(filled, /^PGPASSWORD=.+$/m, `the password was never put in:\n${filled}`);
|
||||
assert.ok(filled.includes(`PGPASSWORD=${onConsumer}`),
|
||||
`the file holds a different password from the credential file:\n${filled}`);
|
||||
assert.match(filled, /^PGUSER=mesh_laptop_meshboard$/m,
|
||||
`the consumer was not told what name to present:\n${filled}`);
|
||||
assert.match(filled, /^PGPORT=5432$/m, `the port did not arrive as a port:\n${filled}`);
|
||||
assert.doesNotMatch(filled, /\$\{/,
|
||||
`a placeholder survived to the machine and would be read as a value:\n${filled}`);
|
||||
assert.match(await must("laptop", `stat -c %a /etc/meshboard/database.env`), /^600/);
|
||||
|
||||
// The password is in that file and nowhere the mesh could read it — which is the whole point of
|
||||
// filling the hole on the machine rather than composing the document in the control plane.
|
||||
|
||||
// And it is nowhere it could have been read on the way. The declaration crossed the broker; the
|
||||
// database is the control plane's; the state is what the node reported back.
|
||||
for (const [machine, where] of [
|
||||
|
||||
Reference in New Issue
Block a user