Use the OpenBao backends¶
Give a process OpenBao-backed state and keys with the least privilege that works.
Before you start¶
-
You need an OpenBao or Vault with a JWT auth mount that accepts the process's projected ServiceAccount token, a KV version 2 mount for state and a transit mount for keys.
-
The policies below were verified against OpenBao 2.6.2. The backends' tests run under them, not under the root token.
Steps¶
1. Create the role¶
Create a JWT role that binds the ServiceAccount subject and audience and names the policies below.
2. Write the policies¶
State on KV version 2, with mount kv and prefix sluis/state:
path "kv/config" { capabilities = ["read"] }
path "kv/data/sluis/state/*" { capabilities = ["create", "update", "read"] }
path "kv/metadata/sluis/state" { capabilities = ["list"] }
path "kv/metadata/sluis/state/*" { capabilities = ["read", "list", "delete"] }
Keys on transit. Give a role only the lines for the operations it performs.
path "transit/keys/audit-data" { capabilities = ["read"] } # the backend reads type, derivation, versions
path "transit/encrypt/audit-data" { capabilities = ["update"] }
path "transit/decrypt/audit-data" { capabilities = ["update"] }
path "transit/datakey/plaintext/audit-data" { capabilities = ["update"] }
path "transit/hmac/audit-data" { capabilities = ["update"] }
path "transit/keys/sluis-signing" { capabilities = ["read"] }
path "transit/sign/sluis-signing" { capabilities = ["update"] }
3. Pin the encryption context¶
The context travels as one base64 string of the canonical JSON of the map, with keys sorted and no white space. Pin it so a compromised role cannot encrypt or decrypt for another instance or purpose.
path "transit/encrypt/audit-data" {
capabilities = ["update"]
required_parameters = ["associated_data"]
allowed_parameters = {
"plaintext" = []
"associated_data" = ["eyJpbnN0YW5jZSI6InByb2QiLCJwdXJwb3NlIjoiY29uY2VhbCJ9"]
}
}
The value above is {"instance":"prod","purpose":"conceal"}. For a derived key, use context in both places. Follow these rules:
-
Add
required_parameters.allowed_parametersalone allows a request that omits the parameter, and on a key that is not derived that request encrypts with no binding. -
Name
plaintextfor encrypt, orciphertextfor decrypt, with an empty list. Otherwise the request is refused. -
Do not pin integer parameters such as
key_version. Pin the key by the path.
4. Point the product at the backends¶
Set the product's state and keys blocks (adapter block reference).
Verify¶
Start a development server and run the backends' conformance tests:
bao server -dev -dev-root-token-id=root -dev-listen-address=127.0.0.1:8200
just test-openbao
Roll back¶
Keep the previous backend's policies until the new one has served traffic. State and keys are separate choices, so move them one at a time.
The policies, line by line, are in the package documentation storage/doc.go.