diff options
| author | Dave Henderson <dhenderson@gmail.com> | 2016-11-19 18:46:48 -0500 |
|---|---|---|
| committer | Dave Henderson <dhenderson@gmail.com> | 2017-03-11 14:03:08 -0500 |
| commit | 55993e7a0a9daadc463e8b2104deec867d630d51 (patch) | |
| tree | 571fa817999395a1a4ccc4756115bab2ab8b0d4a | |
| parent | 28c515090fa99655ab495f1017b86ee4518bb96f (diff) | |
Adding support for GitHub auth strategy for Vault datasources
Signed-off-by: Dave Henderson <dhenderson@gmail.com>
| -rw-r--r-- | README.md | 1 | ||||
| -rw-r--r-- | vault/client.go | 3 | ||||
| -rw-r--r-- | vault/github_strategy.go | 105 | ||||
| -rw-r--r-- | vault/github_strategy_test.go | 70 |
4 files changed, 179 insertions, 0 deletions
@@ -582,6 +582,7 @@ This table describes the currently-supported authentication mechanisms and how t | auth backend | configuration | | ---: |--| | [`app-id`](https://www.vaultproject.io/docs/auth/app-id.html) | Environment variables `$VAULT_APP_ID` and `$VAULT_USER_ID` must be set to the appropriate values.<br/> If the backend is mounted to a different location, set `$VAULT_AUTH_APP_ID_MOUNT`. | +| [`github`](https://www.vaultproject.io/docs/auth/github.html) | Environment variable `$VAULT_AUTH_GITHUB_TOKEN` must be set to an appropriate value.<br/> If the backend is mounted to a different location, set `$VAULT_AUTH_GITHUB_MOUNT`. | | [`token`](https://www.vaultproject.io/docs/auth/token.html) | Determined from either the `$VAULT_TOKEN` environment variable, or read from the file `~/.vault-token` | To use a Vault datasource with a single secret, just use a URL of diff --git a/vault/client.go b/vault/client.go index 1be1ed0b..4dd7235e 100644 --- a/vault/client.go +++ b/vault/client.go @@ -56,6 +56,9 @@ func getAuthStrategy() AuthStrategy { if auth := NewAppIDAuthStrategy(); auth != nil { return auth } + if auth := NewGitHubAuthStrategy(); auth != nil { + return auth + } if auth := NewTokenAuthStrategy(); auth != nil { return auth } diff --git a/vault/github_strategy.go b/vault/github_strategy.go new file mode 100644 index 00000000..772228f7 --- /dev/null +++ b/vault/github_strategy.go @@ -0,0 +1,105 @@ +package vault + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "net/url" + "os" + "time" +) + +// GitHubAuthStrategy - an AuthStrategy that uses Vault's app-id authentication backend. +type GitHubAuthStrategy struct { + Token string `json:"token"` + Mount string `json:"-"` + hc *http.Client +} + +// NewGitHubAuthStrategy - create an AuthStrategy that uses Vault's app-id auth +// backend. +func NewGitHubAuthStrategy() *GitHubAuthStrategy { + mount := os.Getenv("VAULT_AUTH_GITHUB_MOUNT") + if mount == "" { + mount = "github" + } + token := os.Getenv("VAULT_AUTH_GITHUB_TOKEN") + if token != "" { + return &GitHubAuthStrategy{token, mount, nil} + } + return nil +} + +// GetHTTPClient configures the HTTP client with a timeout +func (a *GitHubAuthStrategy) GetHTTPClient() *http.Client { + if a.hc == nil { + a.hc = &http.Client{Timeout: time.Second * 5} + } + return a.hc +} + +// SetToken is a no-op for GitHubAuthStrategy as a token hasn't been acquired yet +func (a *GitHubAuthStrategy) SetToken(req *http.Request) { + // no-op +} + +// Do wraps http.Client.Do +func (a *GitHubAuthStrategy) Do(req *http.Request) (*http.Response, error) { + hc := a.GetHTTPClient() + return hc.Do(req) +} + +// GetToken - log in to the auth backend and return the client token +func (a *GitHubAuthStrategy) GetToken(addr *url.URL) (string, error) { + buf := new(bytes.Buffer) + json.NewEncoder(buf).Encode(&a) + + u := &url.URL{} + *u = *addr + u.Path = "/v1/auth/" + a.Mount + "/login" + res, err := requestAndFollow(a, "POST", u, buf.Bytes()) + if err != nil { + return "", err + } + response := &GitHubAuthResponse{} + err = json.NewDecoder(res.Body).Decode(response) + res.Body.Close() + if err != nil { + return "", err + } + if res.StatusCode != 200 { + err := fmt.Errorf("Unexpected HTTP status %d on GitHub login to %s: %s", res.StatusCode, u, response) + return "", err + } + return response.Auth.ClientToken, nil +} + +// Revokable - +func (a *GitHubAuthStrategy) Revokable() bool { + return true +} + +func (a *GitHubAuthStrategy) String() string { + return fmt.Sprintf("token: %s, mount: %s", a.Token, a.Mount) +} + +// GitHubAuthResponse - the Auth response from /v1/auth/app-id/login +type GitHubAuthResponse struct { + Auth struct { + ClientToken string `json:"client_token"` + LeaseDuration int64 `json:"lease_duration"` + Metadata struct { + Username string `json:"username"` + Org string `json:"org"` + } `json:"metadata"` + Policies []string `json:"policies"` + Renewable bool `json:"renewable"` + } `json:"auth"` +} + +func (a *GitHubAuthResponse) String() string { + buf := new(bytes.Buffer) + json.NewEncoder(buf).Encode(&a) + return string(buf.Bytes()) +} diff --git a/vault/github_strategy_test.go b/vault/github_strategy_test.go new file mode 100644 index 00000000..d7c33cb9 --- /dev/null +++ b/vault/github_strategy_test.go @@ -0,0 +1,70 @@ +package vault + +import ( + "net/url" + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewGitHubAuthStrategy(t *testing.T) { + os.Unsetenv("VAULT_AUTH_GITHUB_TOKEN") + assert.Nil(t, NewGitHubAuthStrategy()) + + os.Setenv("VAULT_AUTH_GITHUB_TOKEN", "foo") + auth := NewGitHubAuthStrategy() + assert.Equal(t, "foo", auth.Token) + assert.Equal(t, "github", auth.Mount) + + os.Setenv("VAULT_AUTH_GITHUB_MOUNT", "bar") + auth = NewGitHubAuthStrategy() + assert.Equal(t, "foo", auth.Token) + assert.Equal(t, "bar", auth.Mount) +} + +func TestGetToken_GitHubErrorsGivenNetworkError(t *testing.T) { + server, client := setupErrorHTTP() + defer server.Close() + + vaultURL, _ := url.Parse("http://vault:8200") + + auth := &GitHubAuthStrategy{"foo", "github", client} + _, err := auth.GetToken(vaultURL) + assert.Error(t, err) +} + +func TestGetToken_GitHubErrorsGivenHTTPErrorStatus(t *testing.T) { + server, client := setupHTTP(500, "application/json; charset=utf-8", `{}`) + defer server.Close() + + vaultURL, _ := url.Parse("http://vault:8200") + + auth := &GitHubAuthStrategy{"foo", "github", client} + _, err := auth.GetToken(vaultURL) + assert.Error(t, err) +} + +func TestGetToken_GitHubErrorsGivenBadJSON(t *testing.T) { + server, client := setupHTTP(200, "application/json; charset=utf-8", `{`) + defer server.Close() + + vaultURL, _ := url.Parse("http://vault:8200") + + auth := &GitHubAuthStrategy{"foo", "github", client} + _, err := auth.GetToken(vaultURL) + assert.Error(t, err) +} + +func TestGetToken_GitHub(t *testing.T) { + server, client := setupHTTP(200, "application/json; charset=utf-8", `{"auth": {"client_token": "baz"}}`) + defer server.Close() + + vaultURL, _ := url.Parse("http://vault:8200") + + auth := &GitHubAuthStrategy{"foo", "github", client} + token, err := auth.GetToken(vaultURL) + assert.NoError(t, err) + + assert.Equal(t, "baz", token) +} |
