Skip to content

Commit

Permalink
Merge pull request #47 from amperity/release/1.0.0
Browse files Browse the repository at this point in the history
1.0.0 Release
  • Loading branch information
drassaby authored Dec 13, 2019
2 parents 2b028c6 + 52a6d5d commit 6bdd2aa
Show file tree
Hide file tree
Showing 22 changed files with 1,507 additions and 424 deletions.
18 changes: 16 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,22 @@ All notable changes to this project will be documented in this file.
This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/).

## [Unreleased]

...

## [1.0.0] - 2019-12-13

**THIS RELEASE CONTAINS SOME BREAKING CHANGES!**

- Large internal refactor that may result in unexpected behavior
[#35](https://github.com/amperity/vault-clj/pull/35)
- Added support for the KV V2 API
[#33](https://github.com/amperity/vault-clj/issues/33)
[#39](https://github.com/amperity/vault-clj/issues/39)
- Added support for externally defined secret engines
[#33](https://github.com/amperity/vault-clj/issues/33)
- Bugfix for mocking delete
[#35](https://github.com/amperity/vault-clj/pull/35)

## [0.7.1] - 2019-11-20

- Bugfix in mock client so that it acts more similarly to http client when creating tokens (create-token!)
Expand Down Expand Up @@ -233,7 +246,8 @@ With this version, the project has been forked to the Amperity organization.
### Added
- Initial library implementation.

[Unreleased]: https://github.com/amperity/vault-clj/compare/0.7.1...HEAD
[Unreleased]: https://github.com/amperity/vault-clj/compare/1.0.0...HEAD
[1.0.0]: https://github.com/amperity/vault-clj/compare/0.7.1...1.0.0
[0.7.1]: https://github.com/amperity/vault-clj/compare/0.7.0...0.7.1
[0.7.0]: https://github.com/amperity/vault-clj/compare/0.6.6...0.7.0
[0.6.6]: https://github.com/amperity/vault-clj/compare/0.6.5...0.6.6
Expand Down
51 changes: 46 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ Leiningen, add the following dependency to your project definition:
:lease-timer nil
:leases #<Atom@640b3e30 {}>}

=> (vault/read-secret client "secret/foo/bar")
; Pull in the secret engine you wish to use:
=> (require '[vault.secrets.kvv1 :as vault-kvv1])

=> (vault-kvv1/read-secret client "secret/foo/bar")
{:data "baz qux"}
```

Expand All @@ -57,10 +60,48 @@ secret fixture data.

=> (def mock-client (vault/new-client "mock:dev/secrets.edn"))

=> (vault/read-secret mock-client "secret/service/foo/login")
; Pull in the secret engine you wish to use:
=> (require '[vault.secrets.kvv1 :as vault-kvv1])

=> (vault-kvv1/read-secret mock-client "secret/service/foo/login")
{:user "foo", :pass "abc123"}
```

## Secret Engines
Vault supports many different [secret engines](https://www.vaultproject.io/docs/secrets/), each with very different
capabilities. For the most part, secret engines behave similar to virtual filesystems, supporting CRUD operations.
Secret engines are very flexible, so please check out the [Vault docs](https://www.vaultproject.io/docs/secrets/)
for more info.

**You should require these for any operations involving secrets in Vault, preferring them to the basic CRUD operations
exposed in `vault.core`**

### Currently Supported Secret Engines

#### [KV V1](https://www.vaultproject.io/docs/secrets/kv/kv-v1.html)

```clojure
(require '[vault.secrets.kvv1 :as vault-kvv1])
```

#### [KV V2](https://www.vaultproject.io/docs/secrets/kv/kv-v2.html)

```clojure
(require '[vault.secrets.kvv2 :as vault-kvv2])
```

### Adding your own Secret Engines
Custom secret engines can be added without contributing to `vault-clj`, but we appreciate PRs adding support for new
engines!

Most operations on Vault secret engines can break down into some combination of logical CRUD operations that the Vault
clients expose. These CRUD operations are outlined by the `vault.core/SecretEngine` protocol. This allows our mocking
to work out of the box for some operations if engines are written to send Vault API calls through the client, as the
diagram below describes:

![vault-clj Multi-engine Support](./vault-clj_multi-engine_support.png)


## Environment Resolution

In order to abstract away the source of sensitive configuration variables
Expand All @@ -82,10 +123,10 @@ client and resolve a map of config variables to their secret values.
[:foo-user :foo-pass :bar])
{:foo-user "foo"
:foo-pass "abc123"
:bar "direct-value}
:bar "direct-value"}
```

## Mount Points
## Auth Mount Points

The auth mount point configuration can be used to address any of the
auth methods under a custom mount point.
Expand All @@ -95,7 +136,7 @@ The auth mount point configuration can be used to address any of the
:auth-mount-point "auth/mountpath/"
:lease-renewal-window 00
:lease-check-period 00
:lease-check-jitter 00))
:lease-check-jitter 00)))
```


Expand Down
3 changes: 2 additions & 1 deletion dev/user.clj
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
[clojure.string :as str]
[clojure.tools.namespace.repl :refer [refresh]]
[com.stuartsierra.component :as component]
[vault.client.http]
[vault.client.mock]
[vault.core :as vault]
(vault.client http mock)
[vault.env :as venv]
[vault.lease :as lease]))

Expand Down
2 changes: 1 addition & 1 deletion project.clj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(defproject amperity/vault-clj "0.7.1"
(defproject amperity/vault-clj "1.0.0"
:description "Clojure client for the Vault secret management system."
:url "https://github.com/amperity/vault-clj"
:license {:name "Apache License"
Expand Down
132 changes: 132 additions & 0 deletions src/vault/authenticate.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
(ns vault.authenticate
"Handles logic relating to the authentication of a Vault client"
(:require
[clojure.string :as str]
[clojure.tools.logging :as log]
[vault.client.api-util :as api-util]
[vault.lease :as lease]))


(defn ^:no-doc api-auth!
"Validate the response from a vault auth call, update auth-ref with additional
tracking state like lease metadata."
[claim auth-ref response]
(let [auth-info (lease/auth-lease (:auth (api-util/clean-body response)))]
(when-not (:client-token auth-info)
(throw (ex-info (str "No client token returned from non-error API response: "
(:status response) " " (:reason-phrase response))
{:body (:body response)})))
(log/info "Successfully authenticated to Vault as %s for policies: %s"
claim (str/join ", " (:policies auth-info)))
(reset! auth-ref auth-info)))


(defmulti authenticate*
"Authenticate the client with vault using the given auth-type and credentials."
(fn [client auth-type credentials] auth-type))


(defmethod authenticate* :default
[client auth-type _]
(throw (ex-info (str "Unsupported auth-type " (pr-str auth-type))
{:auth-type auth-type})))


(defmethod authenticate* :token
[client _ token]
(when-not (string? token)
(throw (IllegalArgumentException. "Token credential must be a string")))
(reset! (:auth client) {:client-token (str/trim token)}))


(defmethod authenticate* :wrap-token
[client _ credentials]
(api-auth!
"wrapped token"
(:auth client)
(api-util/unwrap-secret client credentials)))


(defmethod authenticate* :userpass
[client _ credentials]
(let [{:keys [username password]} credentials]
(api-auth!
(str "user " username)
(:auth client)
(api-util/do-api-request
:post (str (:api-url client) "/v1/auth/userpass/" (:auth-mount-point client) "login/" username)
(merge
(:http-opts client)
{:form-params {:password password}
:content-type :json
:accept :json
:as :json})))))


(defmethod authenticate* :app-id
[client _ credentials]
(let [{:keys [app user]} credentials]
(api-auth!
(str "app-id " app)
(:auth client)
(api-util/do-api-request
:post (str (:api-url client) "/v1/auth/app-id/" (:auth-mount-point client) "login")
(merge
(:http-opts client)
{:form-params {:app_id app, :user_id user}
:content-type :json
:accept :json
:as :json})))))


(defmethod authenticate* :app-role
[client _ credentials]
(let [{:keys [role-id secret-id]} credentials]
(api-auth!
(str "role-id sha256:" (api-util/sha-256 role-id))
(:auth client)
(api-util/do-api-request
:post (str (:api-url client) "/v1/auth/approle/" (:auth-mount-point client) "login")
(merge
(:http-opts client)
{:form-params {:role_id role-id, :secret_id secret-id}
:content-type :json
:accept :json
:as :json})))))


(defmethod authenticate* :ldap
[client _ credentials]
(let [{:keys [username password]} credentials]
(api-auth!
(str "LDAP user " username)
(:auth client)
(api-util/do-api-request
:post (str (:api-url client) "/v1/auth/ldap/" (:auth-mount-point client) "login/" username)
(merge
(:http-opts client)
{:form-params {:password password}
:content-type :json
:accept :json
:as :json})))))


(defmethod authenticate* :k8s
[client _ credentials]
(let [{:keys [api-path jwt role]} credentials
api-path (or api-path (str "/v1/auth/kubernetes/" (:auth-mount-point client) "login"))]
(when-not jwt
(throw (IllegalArgumentException. "Kubernetes auth credentials must include :jwt")))
(when-not role
(throw (IllegalArgumentException. "Kubernetes auth credentials must include :role")))
(api-auth!
(str "Kubernetes auth role=" role)
(:auth client)
(api-util/do-api-request
:post (str (:api-url client) api-path)
(merge
(:http-opts client)
{:form-params {:jwt jwt :role role}
:content-type :json
:accept :json
:as :json})))))
Loading

0 comments on commit 6bdd2aa

Please sign in to comment.