Secrets Backends
wasmCloud hosts retrieve secrets from secrets backends, which communicate with hosts over NATS using specified NATS subject prefixes. Secrets backends conform to a common API so that anyone can write a backend and add an implementation to their cluster. You can learn more about wasmCloud's implementation of secrets in the Secrets page of the Platform Overview.
Implementing a secrets backend
A secrets backend implementation is identified by a single string as a name and will receive credentials requests on a NATS subject that includes this name prefix. The canonical default root prefix for the subject is wasmcloud.secrets
. A specific deploy of a secrets backend is required to set a unique name for the backend and listens for requests on the subject wasmcloud.secrets.$api_version.$backend.$operation
.
A secrets store must handle the following operations (using nats-kv
as an example backend):
Name | Subject | Return Payload |
---|---|---|
get | wasmcloud.secrets.v1alpha1.nats-kv.get | Encrypted SecretResponse |
server_xkey | wasmcloud.secrets.v1alpha1.vault.server_xkey | Unencrypted String |
When an application that makes use of a backend is deployed, you can use the policy block to allow reuse of secrets backend configurations across components and providers.
spec:
policies:
- name: nats-kv
type: policy.secret.wasmcloud.dev/v1alpha1
properties:
backend: nats-kv
See the following section for more details on the secrets backend API.
Secrets backend API
The secrets backend API is a NATS-based API that is used to retrieve secrets from a secrets backend. The API is defined as follows:
#[derive(Serialize, Deserialize)]
struct Context {
// The component or provider's embedded JWT.
jwt: String,
}
struct SecretRequest {
// The name of the secret as addressed in the secret store
name: String,
// The version of the secret
version: Option<String>,
// Context for the request, including application name
context: Context,
}
struct SecretResponse {
pub secret: Option<Secret>,
pub error: Option<String>,
}
struct Secret {
pub name: String,
pub version: String,
pub string_secret: Option<String>,
pub binary_secret: Option<Vec<u8>>,
}
One of the key properties of this API is that sensitive payloads are encrypted in transit over NATS using xkeys. Xkeys are x25519 keypairs that are an extension on top of NATS' nkeys format. These keys are compatible with the NaCl Seal and Open operations, which makes them suitable for general purpose encryption and decryption.
The payload of a request to the wasmcloud.secrets.$name.get
endpoint must be encrypted using a xkey generated by the host for that specific request and provided as a header on the NATS message. This is to prevent replay attacks and eavesdropping by other NATS clients on the secrets backend. If the payload of a get
request was sent in the clear, in theory all a malicious client would need to do in order to get a copy of secrets for a component or provider would be to resend the original request. While the signed component or provider JWTs are not private, having a copy of them does demonstrate that the caller has access to the built binary and accessing secrets on their behalf. Encrypting the payload with a single use xkey prevents the need for a nonce or a timestamp since every request to the endpoint has a unique payload.
The server_xkey
endpoint is used to retrieve the public xkey of the secrets backend so that the host can encrypt and decrypt communication to the backend. Callers should cache this key so that they do not have to make repeated calls to the endpoint before calling get. If the server is unable to decrypt a payload it will return an error indicating that, so clients should make a call to the server_xkey
endpoint to refresh the server's key and try again.
Get request payload
Headers:
Wasmcloud-Host-Xkey
: String
Body:
This payload is encrypted using the xkey received from the backend's server_xkey
response as the recipient.
{
"secret_name": "",
// Optional
"version": "1",
"context": {
"jwt": "..."
}
}
Get reponse payload
This payload is encrypted using the xkey provided in the Wasmcloud-Host-Xkey
header as the recipient.
{
// Will be empty if errored
"secret": {
"name": "",
"version": "",
// Only one of the following fields will be set
"secret_string": "",
"secret_binary": ""
},
// Omitted on success
"error": ""
}
A successful response will only contain the secret payload. An unsuccessful response should contain an error key with a string message describing the error. Examples of what might cause such an error include:
- An invalid source JWT (component or provider)
- Secret not found
- Errors encrypting or decrypting the secret
- Some error with the backend
Secrets backend implementations may also have additional API endpoints as needed—for example, mapping secrets to specific components or providers in the NATS KV-backed implementation.
Secret stores
All wasmCloud stores are secret references which are pieces of config that start with SECRET_
and have the following structure:
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
struct SecretReference {
/// The backend to use for retrieving the secret.
pub backend: String,
/// The key to use for retrieving the secret from the backend.
pub key: String,
/// The version of the secret to retrieve. If not supplied, the latest version will be used.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
}
A NATS KV secret represented in JSON could be named SECRET_my-password
:
{
"backend": "nats-kv",
"key": "my-password"
// optional version
}