Skip to content

vault-courier/vault-courier

Repository files navigation

Vault Courier

Swift client for interacting with Hashicorp Vault and OpenBao.

Hashicorp Vault and OpenBao are both tools for securely storing, auditing and managing access to secrets, such as API tokens, database credentials, and certificates. VaultCourier is a Swift package that can interact with Hashicorp Vault and OpenBao to retrieve and provision secrets. It is built with swift-openapi and pkl.

Features

  • Arbitrary storage of Key/Value secrets (KV-v2)
  • Manage third-party secrets: generate and revoke on-demand credentials for database systems, like PostgreSQL and Valkey
  • Cryptography as a Service (CaaS) via Transit secret engine
  • AppRole Authentication
  • Token Authentication
  • Namespaces: manage isolated secrets from different deployment environments, applications, or teams within a single vault instance.
  • Tracing support
  • Pkl Resource Reader (Enabled with PackageTrait PklSupport)

Usage

Here is a simple example of reading and writing your first secret! First run a Vault in dev mode with

container_id=$(docker run --rm --detach -p 8200:8200 -e 'VAULT_DEV_ROOT_TOKEN_ID=learn-vault' hashicorp/vault:latest)

or with OpenBao

container_id=$(docker run --rm --detach -p 8200:8200 -e 'BAO_DEV_ROOT_TOKEN_ID=learn-vault' openbao/openbao:latest)

then authenticate, write and read the secret.

import VaultCourier
import OpenAPIAsyncHTTPClient
import Foundation

let client = VaultClient(configuration: .defaultHttp(),
                         clientTransport: AsyncHTTPClientTransport())

// Authenticate
try await client.login(method: .token("learn-vault"))

// Write a secret
let keyToSecret = "my-secret-password"
struct Secret: Codable {
    let apiKey: String
}
try await client.writeKeyValue(mountPath: "secret",
                               secret: Secret(apiKey: "secret_api_key"),
                               key: keyToSecret)

print("Secret written successfully")

// Read a Secret
let secret: Secret = try await client.readKeyValueSecret(mountPath: "secret", key: keyToSecret)

print("Access Granted! API Key: \(secret.apiKey)")

To stop the Vault server run:

docker stop "${container_id}" > /dev/null

For a more realistic example that illustrates many important Vault concepts, see the tutorials and examples in the documentation.

Package Traits

Vault has many secret engines and authentication methods that need to be enabled before they can be used. Similarly, in VaultCourier's functionality can be extended by enabling the respective Package traits. Currently we support the following:

To enable an additional trait on the package, update the package dependency:

.package(
    url: "https://github.com/vault-courier/vault-courier",
    .upToNextMinor(from: "0.3.0"),
+   traits: [.defaults, "OtherFeatureSupport"]
)

Available Package traits:

  • PostgresPluginSupport (default): Enable support for Vault-PostgreSQL database plugin HTTP API. Plugin available in Vault and OpenBao. This trait enables DatabaseEngineSupport.
  • ValkeyPluginSupport (default): Enable support for OpenBao-Valkey database plugin HTTP API. This plugin is only available in OpenBao. This trait enables DatabaseEngineSupport.
  • DatabaseEngineSupport (default): Enable basic support for database engine clients.
  • AppRoleSupport (default): Enable AppRole authentication.
  • MockSupport (default): Provides a mock client transport for unit testing and development, and adds Encodable conformance to certain Vault response types.
  • TransitEngineSupport: Provides encryption/decryption as a service. Manage cryptographic keys from a centralized Vault. Sign CSRs and manage certificate chains.
  • PklSupport (experimental): Enable Pkl Resource reader implementations that can read Vault secrets directly from pkl files.
  • ConfigProviderSupport (experimental): Enable a Vault configuration provider. This trait provides a swift-configuration ConfigProvider implementation that can fetch Vault secrets.

API Handlers

The Vault API is covered by sub-clients that specialize in a particular secret engine, authentication method or internal system features. These sub-clients are accessible via handler-methods. For example:

let configuration = PostgresConnectionConfig(connection: "pg-vault")
try await withDatabaseClient(mountPath: "database_eu_central") { client in
    try await client.createPostgresConnection(configuration: configuration)
    try await client.rotateRoot(connection: configuration.connection)
}

These types of handler methods are useful for Vault operators when multiple calls are going to be made to the same group of endpoints, or when functions need to be scoped to a particular mount or child namespace. Application owners will rarely use these handlers, as they simply consume the secrets and the default functionality can be accessed directly from the VaultClient.

The opt-in Vault API is only accessible via an API handler method.

Documentation

You can find reference documentation and user guides for VaultCourier here. The vault-courier-examples repository has a number of examples of different uses of the library.

Security

This library is currently under-development, so we recommend using .upToNextMinor in your Package.swift when depending on vault-courier. It's likely we'll have breaking changes between minors pre-1.0. Please try it out and give us feedback! Please do not use it in production. If you believe you have identified a vulnerability in VaultCourier or any of its related repositories please do not post this in a public forum, do not create a GitHub Issue. Instead please responsibly disclose by contacting us at contact@beamsplitter.co.

License

VaultCourier is available under the Apache 2.0 license.

Acknowledgement

We'd like to give a special thanks to Anastasiya Mudrak for helping us digitize our first logo.

Packages

 
 
 

Contributors

Languages