Skip to main content
Version: 1.x

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):

NameSubjectReturn Payload
getwasmcloud.secrets.v1alpha1.nats-kv.getEncrypted SecretResponse
server_xkeywasmcloud.secrets.v1alpha1.vault.server_xkeyUnencrypted 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.

yaml
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:

rust
#[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.

jsonc
{
  "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.

jsonc
{
  // 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:

rust
#[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:

jsonc
{
  "backend": "nats-kv",
  "key": "my-password"
  // optional version
}