Liking cljdoc? Tell your friends :D

Upgrading from 1.x

If you're upgrading from the 1.x major version, there are a number of differences to account for.

Build and Dependencies

The coordinate for the library has changed from amperity/vault-clj to com.amperity/vault-clj, in keeping with Clojars' new domain verification requirements. Additionally, the dependencies used by the library are much lighter weight now:

  • Use org.clojure/data.json for JSON serialization now instead of cheshire, avoiding messy Jackson dependencies.
  • Dropped dependency on com.stuartsierra/component.
  • Dropped dependency on envoy.

Client Protocols

Many of the protocols and methods previously in vault.core have moved to backend-specific namespaces. For example:

  • TokenManager is now vault.auth.token/API
  • LeaseManager is now vault.sys.leases/API
  • WrappingClient is now vault.sys.wrapping/API
  • SecretEngine is replaced by engine-specific protocols in vault.secret.*

The two previously implemented secrets engines have moved slightly:

  • vault.secrets.kvv1 is now vault.secret.kv.v1
  • vault.secrets.kvv2 is now vault.secret.kv.v2

Authentication

Rather than a single multimethod in vault.authenticate, client authentication is now performed by calling method-specific protocols. For example, if you were previously using the userpass method:

(require '[vault.core :as vault])

(def client (vault/new-client "..."))

(vault/authenticate! client :userpass {:username "bob", :password "hunter2"}

This now looks like:

(require '[vault.client :as vault]
         '[vault.auth.userpass :as userpass])

(def client (vault/new-client "..."))

(userpass/login client "bob" "hunter2")

Renewal and Rotation

In 1.x, the client would renew and rotate secrets as they approached expiry. This was done on a single background thread running as part of the client state. If consumers needed to react to lifecycle events, they could register a "lease watch", which would be invoked when the lease changed. While this worked as a hook for rotated credentials to be updated in whatever system was using them, it was a bit clunky and had some problems:

  • It didn't handle more nuanced outcomes like failures well. Even expiration just called the watch function with nil.
  • The calling code also had to know up front what secret path to register the watch for, coupling it to the underlying Vault API.
  • The watches ran on the same timer thread, so a slow callback could block other watches and even future lease maintenance.

In 2.x, things are a bit different. Instead of a single thread, the client now supports setting a maintenance-executor and a callback-executor. The maintenance executor is responsible for running a periodic task to perform the interactions with Vault, while any callbacks are passed to the callback executor. If not provided, callbacks run on the same thread pool as a regular Clojure future. This gives consumers more control over how these tasks are run, as well as preventing the periodic task from getting blocked.

Instead of a separate method to register callbacks, users can pass a set of callback functions to the method used to read the secret. For example, in vault.secret.database/generate-credentials! a caller can specify :on-renew, :on-rotate, and :on-error functions to handle each outcome. Generally, the rotation callback would replace the previous use of a lease watcher.

Component Lifecycle

For flexibility, the library no longer depends on com.stuartsierra/component and clients no longer implement the Lifecycle protocol. Instead, the lifecycle methods are available as the regular functions vault.client/start and vault.client/stop.

If you want to continue using component as a dependency injection library, you can use the following code to reestablish the previous behavior:

(require '[vault.client :as vault]
         '[com.stuartsierra.component :as component])


(extend vault.client.http.HTTPClient

  component/Lifecycle

  {:start vault/start
   :stop vault/stop})

Environment Resolution

The vault.env environment variable resolution code has been removed to decouple the library from envoy. This can be replicated locally in your project with code like the following:

(require '[clojure.string :as str])


(defn secret-uri?
  [s]
  (and (string? s) (str/starts-with? s "vault:")))


(defn resolve-uri!
  [client vault-uri]
  (let [[path attr] (str/split (subs vault-uri 6) #"#")
        secret (kv/read-secret client path)
        attr (or (keyword attr) :data)
        value (get secret attr)]
    (when (nil? value)
      (throw (ex-info (str "No value for secret " vault-uri)
                      {:path path, :attr attr})))
    value))


(defn resolve-env-secrets!
  [client env]
  (into {}
        (map (fn resolve-var
               [[k v]]
               (if (secret-uri? v)
                 [k (resolve-uri! client v)]
                 [k v])))
        env))

Misc

The client no longer throws an error when you make calls without authentication, to support vault agent usage. #63

Can you improve this documentation?Edit on GitHub

cljdoc is a website building & hosting documentation for Clojure/Script libraries

× close