If you're upgrading from the 1.x major version, there are a number of differences to account for.
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:
org.clojure/data.json
for JSON serialization now instead of cheshire
,
avoiding messy Jackson dependencies.com.stuartsierra/component
.envoy
.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
For the KV secrets engines, previously a list-secrets
call would return a
vector of the keys at that prefix directly. Now, these methods return a map
with a :keys
vector entry if there are secrets present, matching the actual
API response shape.
In 1.x, reading a secret from a customized mount required embedding the mount
prefix in the secret path at read time. Now, each secret engine provides a
with-mount
method which returns an updated client which will perform reads
against the specified mount. This lets customization happen at configuration
time and decouples the code using the client from knowledge of the mount path.
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")
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:
nil
.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.
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})
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))
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