Setting Secure Parameters¶
A secure parameter holds its value one of two ways. It is either sealed
inside DRP with the owning object's key, or it holds a reference — a
LookupUri naming an external secret store such as Vault — so the value lives
outside DRP and DRP keeps only the pointer.
How set decides where the value goes¶
set resolves the parameter before it writes
Every set first resolves the parameter across the inheritance chain —
the object, its profiles, its stage, and finally global.
- If that resolves to a
LookupUri, the value is written into the external store and the object is left untouched. - If it resolves to anything else, or to nothing at all, the value is
written onto the object you named, sealed if the parameter is
Secure.
The reference does not have to be on the object you are setting. One inherited from a profile is followed just the same.
Stated as a table:
| Parameter resolves to | Where the value lands | Object's parameters |
|---|---|---|
A LookupUri |
External store, via the plugin's encrypt action |
unchanged |
| A sealed value | On the addressed object, sealed | key added or replaced |
| A non-secure value | On the addressed object, plaintext | key added or replaced |
| Nothing found | On the addressed object, sealed if Secure |
key added |
Only the first row redirects the write. Every other case writes the object you named — never the profile the value happened to resolve from.
Writing a secure parameter requires the updateSecure object action, whether
it lands in DRP or in an external store.
For how resolution order works, see Parameter Precedence.
Storing a value in DRP¶
With no reference anywhere in the chain, set seals the value onto the object:
{
"Key": "MXu22clcGB9OLkjUveRQSH2KgXbnUg9xh45UKMvh2Us=",
"Nonce": "f26EqUiuN1P7WGMwCbta2ucyQRqcikDQ",
"Payload": "KSWP45zxZYIdRuRnSfr9VoNClZ+jlFg="
}
Read it back with --decode, which requires the getSecure object action:
Storing a value in an external store¶
Establishing a reference¶
Point the parameter at the store by setting it to a SecureData object
containing a LookupUri:
drpcli machines set Name:node1 param auth/password \
to '{"LookupUri":"vault://password?path=testing"}'
Nothing resolved to a reference yet, so by the rule above this is an ordinary write onto the object. The external store is not contacted. A plain read returns the pointer:
URI structure¶
The general shape is:
The scheme is the Name of the plugin instance, not the provider it was
created from. An instance named my-vault is addressed as my-vault:// even
though its provider is vault. That is how one endpoint talks to several
stores at once — vault-prod:// and vault-dr:// are two instances of the
same provider pointing at different servers.
Everything after the scheme is interpreted by that plugin — Vault reads
path=, format=json, and prefix=, while the others take their own. For the
exact fields, see the page for the store you are using, listed under
Secret stores below.
A LookupUri may contain template expansions, in the scheme or anywhere else.
They are evaluated against the machine each time the URI is used, not when it
is stored.
Writing through the reference¶
Now that the parameter resolves to a LookupUri, the same set command writes
to the store instead of the object:
It echoes the reference back rather than a sealed value, because nothing was stored on the machine:
The secret is now in the store:
Writing through a reference never creates one. It only follows a reference that already exists.
Inherited references¶
A reference defined once on a profile applies to every object using it, and
set follows it:
drpcli profiles set prod param auth/password \
to '{"LookupUri":"vault://password?path=shared"}'
drpcli machines addprofile Name:node1 prod
drpcli machines set Name:node1 param auth/password to '"s3cret"'
drpcli machines get Name:node1 param auth/password
# null <- nothing is stored on the machine
drpcli machines get Name:node1 param auth/password --aggregate --decode
# "s3cret"
Use --aggregate on reads when the reference is inherited. Without it, a plain
get looks only at the object's own parameters and returns null.
A constant URI is shared state
vault://password?path=shared resolves to the same location for every
machine using the profile, so one machine's write changes the secret all of
them read. Template the URI when each object should own its own secret:
drpcli profiles set prod param auth/password \
to '{"LookupUri":"vault://password?path=perhost/{{.Machine.Name}}"}' \
--no-write-through
Each machine then writes and reads its own path.
Resolve a machine-templated reference through the machine¶
A templated URI renders against the object you address. {{.Machine.Name}} has a
value on a machine, but not on the profile holding the reference — so read and
write it through the machine:
drpcli machines set Name:node1 param auth/password to '"s3cret"'
drpcli machines get Name:node1 param auth/password --aggregate --decode
The same commands against the profile fail. The reference names one location per machine, and the command names no machine:
Error: param auth/password: cannot expand LookupUri
"vault://password?path=perhost/{{.Machine.Name}}" from profiles prod: ...
Storing or repointing the reference is unaffected — that is a plain value, and
--no-write-through skips resolution entirely. A URI with no template, or one
templated on something the object does have ({{.Param "..."}}), has a single
destination and resolves from the profile as normal.
Writing only locally: --no-write-through¶
--no-write-through skips the resolution step and writes the addressed
object's own parameters. The external store is never contacted.
Use it to pin a local value that shadows an inherited reference:
The machine now holds a sealed value that wins precedence over the profile's reference, and the store is unchanged.
Use it also to change a pointer. Without the flag the parameter already resolves to a reference, so the URI text would be written into the store as if it were the secret:
drpcli machines set Name:node1 param auth/password \
to '{"LookupUri":"vault://password?path=elsewhere"}' --no-write-through
add and remove¶
Neither resolves the chain, so neither writes through.
add always writes the addressed object and fails if the key is already
present there. It succeeds when an object only inherits a value, storing a
local one that shadows the inherited reference.
remove deletes the object's own value. It never deletes anything from the
external store, and it returns not-found if the object only inherits the
parameter.
REST equivalents¶
GET /api/v3/machines/{uuid}/params/{key}?aggregate=true&decode=true
POST /api/v3/machines/{uuid}/params/{key}
POST /api/v3/machines/{uuid}/params/{key}?no-write-through=true
Endpoints that resolve before writing advertise the
secure-param-write-through feature flag in GET /api/v3/info.
Secret stores¶
Each store is provided by a plugin, installed from the catalog and configured
by an administrator. The plugin's Name is the URI scheme; its page documents
the fields that follow.
- Vault — HashiCorp Vault. Reading and writing cover the URI fields (
path=,format=json,prefix=). - AWS Secman — AWS Secrets Manager, using the AWS credential file on the DRP host.
- Azure KeyVault — Azure Key Vault secrets and certificates, using Azure CLI credentials on the DRP host.
- Command KeyVault — any CLI-accessible store, via named shell commands set in
cmdvault/commands.
Secure Parameter Store is the administrator's overview of the four.
References¶
- Secure Parameter Architecture — how sealing and plugin dispatch work
- Parameter Precedence — the resolution order
setuses - Secure Parameter Store — configuring an external store