From 7d5209c251bc632a1ecd4831c5d32a2f84eb67ae Mon Sep 17 00:00:00 2001 From: Adam Greene Date: Tue, 5 Jul 2016 13:21:56 -0700 Subject: [PATCH 01/30] Update aws-ec2.html.md per #1582, updating the docs to include notes about pkcs#7 handling, specifically that aws returns the pkcs#7 cert with newlines and that they need to be stripped before sending them to the login endpoint --- website/source/docs/auth/aws-ec2.html.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/source/docs/auth/aws-ec2.html.md b/website/source/docs/auth/aws-ec2.html.md index 25879893d9..4c99d89e4b 100644 --- a/website/source/docs/auth/aws-ec2.html.md +++ b/website/source/docs/auth/aws-ec2.html.md @@ -313,7 +313,7 @@ curl -X POST -H "x-vault-token:123" "http://127.0.0.1:8200/v1/auth/aws-ec2/role/ #### Perform the login operation ``` -curl -X POST "http://127.0.0.1:8200/v1/auth/aws-ec2/login" -d '{"role":"dev-role","pkcs7":"MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAaCAJIAEggGmewogICJkZXZwYXlQcm9kdWN0Q29kZXMiIDogbnVsbCwKICAicHJpdmF0ZUlwIiA6ICIxNzIuMzEuNjMuNjAiLAogICJhdmFpbGFiaWxpdHlab25lIiA6ICJ1cy1lYXN0LTFjIiwKICAidmVyc2lvbiIgOiAiMjAxMC0wOC0zMSIsCiAgImluc3RhbmNlSWQiIDogImktZGUwZjEzNDQiLAogICJiaWxsaW5nUHJvZHVjdHMiIDogbnVsbCwKICAiaW5zdGFuY2VUeXBlIiA6ICJ0Mi5taWNybyIsCiAgImFjY291bnRJZCIgOiAiMjQxNjU2NjE1ODU5IiwKICAiaW1hZ2VJZCIgOiAiYW1pLWZjZTNjNjk2IiwKICAicGVuZGluZ1RpbWUiIDogIjIwMTYtMDQtMDVUMTY6MjY6NTVaIiwKICAiYXJjaGl0ZWN0dXJlIiA6ICJ4ODZfNjQiLAogICJrZXJuZWxJZCIgOiBudWxsLAogICJyYW1kaXNrSWQiIDogbnVsbCwKICAicmVnaW9uIiA6ICJ1cy1lYXN0LTEiCn0AAAAAAAAxggEXMIIBEwIBATBpMFwxCzAJBgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQwIJAJa6SNnlXhpnMAkGBSsOAwIaBQCgXTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xNjA0MDUxNjI3MDBaMCMGCSqGSIb3DQEJBDEWBBRtiynzMTNfTw1TV/d8NvfgVw+XfTAJBgcqhkjOOAQDBC4wLAIUVfpVcNYoOKzN1c+h1Vsm/c5U0tQCFAK/K72idWrONIqMOVJ8Uen0wYg4AAAAAAAA","nonce":"vault-client-nonce"}' +curl -X POST "http://127.0.0.1:8200/v1/auth/aws-ec2/login" -d '{"role":"dev-role","pkcs7":"$(curl -s http://169.254.169.254/latest/dynamic/instance-identity/pkcs7 | tr -d '\n')","nonce":"vault-client-nonce"}' ``` @@ -1107,7 +1107,7 @@ in its identity document to match the one specified by this parameter.
  • pkcs7 required - PKCS7 signature of the identity document. + PKCS7 signature of the identity document with all `\n` characters removed.
  • From 877a7dc378bb371f0d561069933296b040b13b7c Mon Sep 17 00:00:00 2001 From: Brian Shumate Date: Wed, 6 Jul 2016 10:02:52 -0400 Subject: [PATCH 04/30] Minor grammar edit --- website/source/intro/index.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/intro/index.html.markdown b/website/source/intro/index.html.markdown index 5c75f31647..37644fa2a9 100644 --- a/website/source/intro/index.html.markdown +++ b/website/source/intro/index.html.markdown @@ -56,7 +56,7 @@ The key features of Vault are: having to design their own encryption methods. * **Leasing and Renewal**: All secrets in Vault have a _lease_ associated - with it. At the end of the lease, Vault will automatically revoke that + with them. At the end of the lease, Vault will automatically revoke that secret. Clients are able to renew leases via built-in renew APIs. * **Revocation**: Vault has built-in support for secret revocation. Vault From 60df9d3461347d5043588d44d7288bd6a9c59d62 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Wed, 6 Jul 2016 16:42:34 -0400 Subject: [PATCH 05/30] Make the API client retry on 5xx errors. This should help with transient issues. Full control over min/max delays and number of retries (and ability to turn off) is provided in the API and via env vars. Fix tests. --- api/client.go | 100 +++++++++++++++++++++++++++++++++---- api/client_test.go | 34 +++++++++---- api/request.go | 23 ++++++--- logical/testing/testing.go | 34 +++++++------ vault/expiration.go | 2 +- vault/expiration_test.go | 14 ++++-- 6 files changed, 159 insertions(+), 48 deletions(-) diff --git a/api/client.go b/api/client.go index 70bc5e00e5..f7036ccc89 100644 --- a/api/client.go +++ b/api/client.go @@ -13,6 +13,7 @@ import ( "time" "github.com/hashicorp/go-cleanhttp" + "github.com/hashicorp/go-retryablehttp" "github.com/hashicorp/go-rootcerts" ) @@ -24,6 +25,9 @@ const EnvVaultClientKey = "VAULT_CLIENT_KEY" const EnvVaultInsecure = "VAULT_SKIP_VERIFY" const EnvVaultTLSServerName = "VAULT_TLS_SERVER_NAME" const EnvVaultWrapTTL = "VAULT_WRAP_TTL" +const EnvVaultRetryWaitMin = "VAULT_RETRY_WAIT_MIN" +const EnvVaultRetryWaitMax = "VAULT_RETRY_WAIT_MAX" +const EnvVaultRetryMax = "VAULT_RETRY_MAX" var ( errRedirect = errors.New("redirect") @@ -49,6 +53,18 @@ type Config struct { HttpClient *http.Client redirectSetup sync.Once + + // RetryWaitMin controls the minimum amount of time to wait between retries + // when a 5xx error occurs + RetryWaitMin time.Duration + + // RetryWaitMax controls the maximum amount of time to wait between retries + // when a 5xx error occurs + RetryWaitMax time.Duration + + // RetryMax controls the maximum number of times to retry when a 5xx error + // occurs. Set to 0 to disable retrying. + RetryMax int } // DefaultConfig returns a default configuration for the client. It is @@ -73,6 +89,10 @@ func DefaultConfig() *Config { config.Address = v } + config.RetryWaitMin = 1 * time.Second + config.RetryWaitMax = 30 * time.Second + config.RetryMax = 15 + return config } @@ -89,6 +109,10 @@ func (c *Config) ReadEnvironment() error { var foundInsecure bool var envTLSServerName string + var envRetryWaitMin *time.Duration + var envRetryWaitMax *time.Duration + var envRetryMax *uint64 + var clientCert tls.Certificate var foundClientCert bool var err error @@ -96,6 +120,44 @@ func (c *Config) ReadEnvironment() error { if v := os.Getenv(EnvVaultAddress); v != "" { envAddress = v } + + // Handle retry parameters + { + if v := os.Getenv(EnvVaultRetryWaitMin); v != "" { + waitMin, err := time.ParseDuration(v) + if err != nil { + return err + } + envRetryWaitMin = &waitMin + } + if v := os.Getenv(EnvVaultRetryWaitMax); v != "" { + waitMax, err := time.ParseDuration(v) + if err != nil { + return err + } + envRetryWaitMax = &waitMax + } + if v := os.Getenv(EnvVaultRetryMax); v != "" { + retryMax, err := strconv.ParseUint(v, 10, 32) + if err != nil { + return err + } + envRetryMax = &retryMax + } + + min := c.RetryWaitMin + if envRetryWaitMin != nil { + min = *envRetryWaitMin + } + max := c.RetryWaitMax + if envRetryWaitMax != nil { + max = *envRetryWaitMax + } + if min > max { + return fmt.Errorf("Maximum retry delay is less than minimum retry delay") + } + } + if v := os.Getenv(EnvVaultCACert); v != "" { envCACert = v } @@ -132,11 +194,30 @@ func (c *Config) ReadEnvironment() error { } } + clientTLSConfig := c.HttpClient.Transport.(*http.Transport).TLSClientConfig + rootConfig := &rootcerts.Config{ + CAFile: envCACert, + CAPath: envCAPath, + } + err = rootcerts.ConfigureTLS(clientTLSConfig, rootConfig) + if err != nil { + return err + } + if envAddress != "" { c.Address = envAddress } - clientTLSConfig := c.HttpClient.Transport.(*http.Transport).TLSClientConfig + if envRetryWaitMin != nil { + c.RetryWaitMin = *envRetryWaitMin + } + if envRetryWaitMax != nil { + c.RetryWaitMax = *envRetryWaitMax + } + if envRetryMax != nil { + c.RetryMax = int(*envRetryMax) + } + if foundInsecure { clientTLSConfig.InsecureSkipVerify = envInsecure } @@ -148,15 +229,6 @@ func (c *Config) ReadEnvironment() error { clientTLSConfig.ServerName = envTLSServerName } - rootConfig := &rootcerts.Config{ - CAFile: envCACert, - CAPath: envCAPath, - } - err = rootcerts.ConfigureTLS(clientTLSConfig, rootConfig) - if err != nil { - return err - } - return nil } @@ -273,8 +345,14 @@ START: return nil, err } + client := retryablehttp.NewClient() + client.HTTPClient = c.config.HttpClient + client.RetryWaitMax = c.config.RetryWaitMax + client.RetryWaitMin = c.config.RetryWaitMin + client.RetryMax = c.config.RetryMax + var result *Response - resp, err := c.config.HttpClient.Do(req) + resp, err := client.Do(req) if resp != nil { result = &Response{Response: resp} } diff --git a/api/client_test.go b/api/client_test.go index a20add0057..2d6df0ea5e 100644 --- a/api/client_test.go +++ b/api/client_test.go @@ -107,16 +107,25 @@ func TestClientEnvSettings(t *testing.T) { oldClientCert := os.Getenv(EnvVaultClientCert) oldClientKey := os.Getenv(EnvVaultClientKey) oldSkipVerify := os.Getenv(EnvVaultInsecure) - os.Setenv("VAULT_CACERT", cwd+"/test-fixtures/keys/cert.pem") - os.Setenv("VAULT_CAPATH", cwd+"/test-fixtures/keys") - os.Setenv("VAULT_CLIENT_CERT", cwd+"/test-fixtures/keys/cert.pem") - os.Setenv("VAULT_CLIENT_KEY", cwd+"/test-fixtures/keys/key.pem") - os.Setenv("VAULT_SKIP_VERIFY", "true") - defer os.Setenv("VAULT_CACERT", oldCACert) - defer os.Setenv("VAULT_CAPATH", oldCAPath) - defer os.Setenv("VAULT_CLIENT_CERT", oldClientCert) - defer os.Setenv("VAULT_CLIENT_KEY", oldClientKey) - defer os.Setenv("VAULT_SKIP_VERIFY", oldSkipVerify) + oldRetryWaitMin := os.Getenv(EnvVaultRetryWaitMin) + oldRetryWaitMax := os.Getenv(EnvVaultRetryWaitMax) + oldRetryMax := os.Getenv(EnvVaultRetryMax) + os.Setenv(EnvVaultCACert, cwd+"/test-fixtures/keys/cert.pem") + os.Setenv(EnvVaultCAPath, cwd+"/test-fixtures/keys") + os.Setenv(EnvVaultClientCert, cwd+"/test-fixtures/keys/cert.pem") + os.Setenv(EnvVaultClientKey, cwd+"/test-fixtures/keys/key.pem") + os.Setenv(EnvVaultInsecure, "true") + os.Setenv(EnvVaultRetryWaitMin, "20s") + os.Setenv(EnvVaultRetryWaitMax, "25s") + os.Setenv(EnvVaultRetryMax, "20") + defer os.Setenv(EnvVaultCACert, oldCACert) + defer os.Setenv(EnvVaultCAPath, oldCAPath) + defer os.Setenv(EnvVaultClientCert, oldClientCert) + defer os.Setenv(EnvVaultClientKey, oldClientKey) + defer os.Setenv(EnvVaultInsecure, oldSkipVerify) + defer os.Setenv(EnvVaultRetryWaitMin, oldRetryWaitMin) + defer os.Setenv(EnvVaultRetryWaitMax, oldRetryWaitMax) + defer os.Setenv(EnvVaultRetryMax, oldRetryMax) config := DefaultConfig() if err := config.ReadEnvironment(); err != nil { @@ -133,4 +142,9 @@ func TestClientEnvSettings(t *testing.T) { if tlsConfig.InsecureSkipVerify != true { t.Fatalf("bad: %v", tlsConfig.InsecureSkipVerify) } + + os.Setenv(EnvVaultRetryWaitMax, "15s") + if err := config.ReadEnvironment(); err == nil { + t.Fatal("expected error due to max retry time being less than min") + } } diff --git a/api/request.go b/api/request.go index 8f22dd5725..16062e7cf0 100644 --- a/api/request.go +++ b/api/request.go @@ -3,9 +3,11 @@ package api import ( "bytes" "encoding/json" + "fmt" "io" - "net/http" "net/url" + + "github.com/hashicorp/go-retryablehttp" ) // Request is a raw request configuration structure used to initiate @@ -43,14 +45,23 @@ func (r *Request) ResetJSONBody() error { return r.SetJSONBody(r.Obj) } -// ToHTTP turns this request into a valid *http.Request for use with the -// net/http package. -func (r *Request) ToHTTP() (*http.Request, error) { +// ToHTTP turns this request into a *retryablehttp.Request +func (r *Request) ToHTTP() (*retryablehttp.Request, error) { // Encode the query parameters r.URL.RawQuery = r.Params.Encode() - // Create the HTTP request - req, err := http.NewRequest(r.Method, r.URL.RequestURI(), r.Body) + // Create the HTTP request; retryable needs a ReadSeeker + body := bytes.NewBuffer(nil) + if r.Body != nil { + n, err := body.ReadFrom(r.Body) + if err != nil { + return nil, err + } + if n != r.BodySize { + return nil, fmt.Errorf("Could not read full body size from Request") + } + } + req, err := retryablehttp.NewRequest(r.Method, r.URL.RequestURI(), bytes.NewReader(body.Bytes())) if err != nil { return nil, err } diff --git a/logical/testing/testing.go b/logical/testing/testing.go index 71dbf12f19..bd0e68dc3a 100644 --- a/logical/testing/testing.go +++ b/logical/testing/testing.go @@ -108,11 +108,11 @@ type TestTeardownFunc func() error // the "-test.v" flag) is set. Because some acceptance tests take quite // long, we require the verbose flag so users are able to see progress // output. -func Test(t TestT, c TestCase) { +func Test(tt TestT, c TestCase) { // We only run acceptance tests if an env var is set because they're // slow and generally require some outside configuration. if c.AcceptanceTest && os.Getenv(TestEnvVar) == "" { - t.Skip(fmt.Sprintf( + tt.Skip(fmt.Sprintf( "Acceptance tests skipped unless env '%s' set", TestEnvVar)) return @@ -120,7 +120,7 @@ func Test(t TestT, c TestCase) { // We require verbose mode so that the user knows what is going on. if c.AcceptanceTest && !testTesting && !testing.Verbose() { - t.Fatal("Acceptance tests must be run with the -v flag on tests") + tt.Fatal("Acceptance tests must be run with the -v flag on tests") return } @@ -131,7 +131,8 @@ func Test(t TestT, c TestCase) { // Check that something is provided if c.Backend == nil && c.Factory == nil { - t.Fatal("Must provide either Backend or Factory") + tt.Fatal("Must provide either Backend or Factory") + return } // Create an in-memory Vault core @@ -148,7 +149,7 @@ func Test(t TestT, c TestCase) { DisableMlock: true, }) if err != nil { - t.Fatal("error initializing core: ", err) + tt.Fatal("error initializing core: ", err) return } @@ -158,15 +159,16 @@ func Test(t TestT, c TestCase) { SecretThreshold: 1, }, nil) if err != nil { - t.Fatal("error initializing core: ", err) + tt.Fatal("error initializing core: ", err) + return } // Unseal the core if unsealed, err := core.Unseal(init.SecretShares[0]); err != nil { - t.Fatal("error unsealing core: ", err) + tt.Fatal("error unsealing core: ", err) return } else if !unsealed { - t.Fatal("vault shouldn't be sealed") + tt.Fatal("vault shouldn't be sealed") return } @@ -177,7 +179,7 @@ func Test(t TestT, c TestCase) { clientConfig.Address = addr client, err := api.NewClient(clientConfig) if err != nil { - t.Fatal("error initializing HTTP client: ", err) + tt.Fatal("error initializing HTTP client: ", err) return } @@ -191,7 +193,7 @@ func Test(t TestT, c TestCase) { Description: "acceptance test", } if err := client.Sys().Mount(prefix, mountInfo); err != nil { - t.Fatal("error mounting backend: ", err) + tt.Fatal("error mounting backend: ", err) return } @@ -220,7 +222,7 @@ func Test(t TestT, c TestCase) { ct := req.ClientToken req.ClientToken = "" if err := s.PreFlight(req); err != nil { - t.Error(fmt.Sprintf("Failed preflight for step %d: %s", i+1, err)) + tt.Error(fmt.Sprintf("Failed preflight for step %d: %s", i+1, err)) break } req.ClientToken = ct @@ -249,7 +251,7 @@ func Test(t TestT, c TestCase) { err = nil } else { // If the error is not expected, fail right away. - t.Error(fmt.Sprintf("Failed step %d: %s", i+1, err)) + tt.Error(fmt.Sprintf("Failed step %d: %s", i+1, err)) break } } @@ -272,7 +274,7 @@ func Test(t TestT, c TestCase) { } if err != nil { - t.Error(fmt.Sprintf("Failed step %d: %s", i+1, err)) + tt.Error(fmt.Sprintf("Failed step %d: %s", i+1, err)) break } } @@ -288,7 +290,7 @@ func Test(t TestT, c TestCase) { } if err != nil { failedRevokes = append(failedRevokes, req.Secret) - t.Error(fmt.Sprintf("[ERR] Revoke error: %s", err)) + tt.Error(fmt.Sprintf("[ERR] Revoke error: %s", err)) } } @@ -305,14 +307,14 @@ func Test(t TestT, c TestCase) { } if err != nil { if !errwrap.Contains(err, logical.ErrUnsupportedOperation.Error()) { - t.Error(fmt.Sprintf("[ERR] Rollback error: %s", err)) + tt.Error(fmt.Sprintf("[ERR] Rollback error: %s", err)) } } // If we have any failed revokes, log it. if len(failedRevokes) > 0 { for _, s := range failedRevokes { - t.Error(fmt.Sprintf( + tt.Error(fmt.Sprintf( "WARNING: Revoking the following secret failed. It may\n"+ "still exist. Please verify:\n\n%#v", s)) diff --git a/vault/expiration.go b/vault/expiration.go index 0b56135bad..883fca4fef 100644 --- a/vault/expiration.go +++ b/vault/expiration.go @@ -363,7 +363,7 @@ func (m *ExpirationManager) RenewToken(req *logical.Request, source string, toke // Check if the lease is renewable. Note that this also checks for a nil // lease and errors in that case as well. if err := le.renewable(); err != nil { - return nil, err + return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest } // Attempt to renew the auth entry diff --git a/vault/expiration_test.go b/vault/expiration_test.go index c5a4f8004b..c3940682ad 100644 --- a/vault/expiration_test.go +++ b/vault/expiration_test.go @@ -158,8 +158,11 @@ func TestExpiration_RegisterAuth_NoLease(t *testing.T) { } // Should not be able to renew, no expiration - _, err = exp.RenewToken(&logical.Request{}, "auth/github/login", root.ID, 0) - if err.Error() != "lease not found or lease is not renewable" { + resp, err := exp.RenewToken(&logical.Request{}, "auth/github/login", root.ID, 0) + if resp == nil { + t.Fatal("expected a response") + } + if err != logical.ErrInvalidRequest || resp.Error().Error() != "lease not found or lease is not renewable" { t.Fatalf("err: %v", err) } @@ -455,8 +458,11 @@ func TestExpiration_RenewToken_NotRenewable(t *testing.T) { } // Attempt to renew the token - _, err = exp.RenewToken(&logical.Request{}, "auth/github/login", root.ID, 0) - if err.Error() != "lease is not renewable" { + resp, err := exp.RenewToken(&logical.Request{}, "auth/github/login", root.ID, 0) + if resp == nil { + t.Fatal("expected a response") + } + if err != logical.ErrInvalidRequest || resp.Error().Error() != "lease is not renewable" { t.Fatalf("err: %v", err) } } From c7e59ffe286a90f33935fb7fd53e4a2930e0d000 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Wed, 6 Jul 2016 19:00:23 -0400 Subject: [PATCH 06/30] Fix upgrade to 0.6 docs --- website/source/layouts/install.erb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/source/layouts/install.erb b/website/source/layouts/install.erb index 83fa5007ef..6b2274ce58 100644 --- a/website/source/layouts/install.erb +++ b/website/source/layouts/install.erb @@ -25,8 +25,8 @@ > Upgrade to 0.5.1 - > - Upgrade to 0.6.0 + > + Upgrade to 0.6 From 28ed4f4c9b922cf54f0cbd5ecb78b4c6d69adef1 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 7 Jul 2016 10:42:08 -0400 Subject: [PATCH 07/30] Add go-retryablehttp dep --- .../hashicorp/go-retryablehttp/LICENSE | 363 ++++++++++++++++++ .../hashicorp/go-retryablehttp/Makefile | 11 + .../hashicorp/go-retryablehttp/README.md | 43 +++ .../hashicorp/go-retryablehttp/client.go | 234 +++++++++++ vendor/vendor.json | 6 + 5 files changed, 657 insertions(+) create mode 100644 vendor/github.com/hashicorp/go-retryablehttp/LICENSE create mode 100644 vendor/github.com/hashicorp/go-retryablehttp/Makefile create mode 100644 vendor/github.com/hashicorp/go-retryablehttp/README.md create mode 100644 vendor/github.com/hashicorp/go-retryablehttp/client.go diff --git a/vendor/github.com/hashicorp/go-retryablehttp/LICENSE b/vendor/github.com/hashicorp/go-retryablehttp/LICENSE new file mode 100644 index 0000000000..e87a115e46 --- /dev/null +++ b/vendor/github.com/hashicorp/go-retryablehttp/LICENSE @@ -0,0 +1,363 @@ +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. "Contributor" + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. "Contributor Version" + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the terms of + a Secondary License. + +1.6. "Executable Form" + + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + + means a work that combines Covered Software with other material, in a + separate file or files, that is not Covered Software. + +1.8. "License" + + means this document. + +1.9. "Licensable" + + means having the right to grant, to the maximum extent possible, whether + at the time of the initial grant or subsequently, any and all of the + rights conveyed by this License. + +1.10. "Modifications" + + means any of the following: + + a. any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. "Patent Claims" of a Contributor + + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the License, + by the making, using, selling, offering for sale, having made, import, + or transfer of either its Contributions or its Contributor Version. + +1.12. "Secondary License" + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. "Source Code Form" + + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, "control" means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution + become effective for each Contribution on the date the Contributor first + distributes such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under + this License. No additional rights or licenses will be implied from the + distribution or licensing of Covered Software under this License. + Notwithstanding Section 2.1(b) above, no patent license is granted by a + Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of + its Contributions. + + This License does not grant any rights in the trademarks, service marks, + or logos of any Contributor (except as may be necessary to comply with + the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this + License (see Section 10.2) or under the terms of a Secondary License (if + permitted under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its + Contributions are its original creation(s) or it has sufficient rights to + grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under + applicable copyright doctrines of fair use, fair dealing, or other + equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under + the terms of this License. You must inform recipients that the Source + Code Form of the Covered Software is governed by the terms of this + License, and how they can obtain a copy of this License. You may not + attempt to alter or restrict the recipients' rights in the Source Code + Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter the + recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for + the Covered Software. If the Larger Work is a combination of Covered + Software with a work governed by one or more Secondary Licenses, and the + Covered Software is not Incompatible With Secondary Licenses, this + License permits You to additionally distribute such Covered Software + under the terms of such Secondary License(s), so that the recipient of + the Larger Work may, at their option, further distribute the Covered + Software under the terms of either this License or such Secondary + License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices + (including copyright notices, patent notices, disclaimers of warranty, or + limitations of liability) contained within the Source Code Form of the + Covered Software, except that You may alter any license notices to the + extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on + behalf of any Contributor. You must make it absolutely clear that any + such warranty, support, indemnity, or liability obligation is offered by + You alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, + judicial order, or regulation then You must: (a) comply with the terms of + this License to the maximum extent possible; and (b) describe the + limitations and the code they affect. Such description must be placed in a + text file included with all distributions of the Covered Software under + this License. Except to the extent prohibited by statute or regulation, + such description must be sufficiently detailed for a recipient of ordinary + skill to be able to understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing + basis, if such Contributor fails to notify You of the non-compliance by + some reasonable means prior to 60 days after You have come back into + compliance. Moreover, Your grants from a particular Contributor are + reinstated on an ongoing basis if such Contributor notifies You of the + non-compliance by some reasonable means, this is the first time You have + received notice of non-compliance with this License from such + Contributor, and You become compliant prior to 30 days after Your receipt + of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, + counter-claims, and cross-claims) alleging that a Contributor Version + directly or indirectly infringes any patent, then the rights granted to + You by any and all Contributors for the Covered Software under Section + 2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an "as is" basis, + without warranty of any kind, either expressed, implied, or statutory, + including, without limitation, warranties that the Covered Software is free + of defects, merchantable, fit for a particular purpose or non-infringing. + The entire risk as to the quality and performance of the Covered Software + is with You. Should any Covered Software prove defective in any respect, + You (not any Contributor) assume the cost of any necessary servicing, + repair, or correction. This disclaimer of warranty constitutes an essential + part of this License. No use of any Covered Software is authorized under + this License except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from + such party's negligence to the extent applicable law prohibits such + limitation. Some jurisdictions do not allow the exclusion or limitation of + incidental or consequential damages, so this exclusion and limitation may + not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts + of a jurisdiction where the defendant maintains its principal place of + business and such litigation shall be governed by laws of that + jurisdiction, without reference to its conflict-of-law provisions. Nothing + in this Section shall prevent a party's ability to bring cross-claims or + counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. Any law or regulation which provides that + the language of a contract shall be construed against the drafter shall not + be used to construe this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version + of the License under which You originally received the Covered Software, + or under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a + modified version of this License if you rename the license and remove + any references to the name of the license steward (except to note that + such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary + Licenses If You choose to distribute Source Code Form that is + Incompatible With Secondary Licenses under the terms of this version of + the License, the notice described in Exhibit B of this License must be + attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, +then You may include the notice in a location (such as a LICENSE file in a +relevant directory) where a recipient would be likely to look for such a +notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice + + This Source Code Form is "Incompatible + With Secondary Licenses", as defined by + the Mozilla Public License, v. 2.0. + diff --git a/vendor/github.com/hashicorp/go-retryablehttp/Makefile b/vendor/github.com/hashicorp/go-retryablehttp/Makefile new file mode 100644 index 0000000000..da17640e64 --- /dev/null +++ b/vendor/github.com/hashicorp/go-retryablehttp/Makefile @@ -0,0 +1,11 @@ +default: test + +test: + go vet ./... + go test -race ./... + +updatedeps: + go get -f -t -u ./... + go get -f -u ./... + +.PHONY: default test updatedeps diff --git a/vendor/github.com/hashicorp/go-retryablehttp/README.md b/vendor/github.com/hashicorp/go-retryablehttp/README.md new file mode 100644 index 0000000000..0d6f9ed40a --- /dev/null +++ b/vendor/github.com/hashicorp/go-retryablehttp/README.md @@ -0,0 +1,43 @@ +go-retryablehttp +================ + +[![Build Status](http://img.shields.io/travis/hashicorp/go-retryablehttp.svg?style=flat-square)][travis] +[![Go Documentation](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)][godocs] + +[travis]: http://travis-ci.org/hashicorp/go-retryablehttp +[godocs]: http://godoc.org/github.com/hashicorp/go-retryablehttp + +The `retryablehttp` package provides a familiar HTTP client interface with +automatic retries and exponential backoff. It is a thin wrapper over the +standard `net/http` client library and exposes nearly the same public API. This +makes `retryablehttp` very easy to drop into existing programs. + +`retryablehttp` performs automatic retries under certain conditions. Mainly, if +an error is returned by the client (connection errors, etc.), or if a 500-range +response code is received, then a retry is invoked after a wait period. +Otherwise, the response is returned and left to the caller to interpret. + +The main difference from `net/http` is that requests which take a request body +(POST/PUT et. al) require an `io.ReadSeeker` to be provided. This enables the +request body to be "rewound" if the initial request fails so that the full +request can be attempted again. + +Example Use +=========== + +Using this library should look almost identical to what you would do with +`net/http`. The most simple example of a GET request is shown below: + +```go +resp, err := retryablehttp.Get("/foo") +if err != nil { + panic(err) +} +``` + +The returned response object is an `*http.Response`, the same thing you would +usually get from `net/http`. Had the request failed one or more times, the above +call would block and retry with exponential backoff. + +For more usage and examples see the +[godoc](http://godoc.org/github.com/hashicorp/go-retryablehttp). diff --git a/vendor/github.com/hashicorp/go-retryablehttp/client.go b/vendor/github.com/hashicorp/go-retryablehttp/client.go new file mode 100644 index 0000000000..1a7df1f489 --- /dev/null +++ b/vendor/github.com/hashicorp/go-retryablehttp/client.go @@ -0,0 +1,234 @@ +// The retryablehttp package provides a familiar HTTP client interface with +// automatic retries and exponential backoff. It is a thin wrapper over the +// standard net/http client library and exposes nearly the same public API. +// This makes retryablehttp very easy to drop into existing programs. +// +// retryablehttp performs automatic retries under certain conditions. Mainly, if +// an error is returned by the client (connection errors etc), or if a 500-range +// response is received, then a retry is invoked. Otherwise, the response is +// returned and left to the caller to interpret. +// +// The main difference from net/http is that requests which take a request body +// (POST/PUT et. al) require an io.ReadSeeker to be provided. This enables the +// request body to be "rewound" if the initial request fails so that the full +// request can be attempted again. +package retryablehttp + +import ( + "fmt" + "io" + "io/ioutil" + "log" + "math" + "net/http" + "net/url" + "os" + "strings" + "time" + + "github.com/hashicorp/go-cleanhttp" +) + +var ( + // Default retry configuration + defaultRetryWaitMin = 1 * time.Second + defaultRetryWaitMax = 5 * time.Minute + defaultRetryMax = 32 + + // defaultClient is used for performing requests without explicitly making + // a new client. It is purposely private to avoid modifications. + defaultClient = NewClient() +) + +// LenReader is an interface implemented by many in-memory io.Reader's. Used +// for automatically sending the right Content-Length header when possible. +type LenReader interface { + Len() int +} + +// Request wraps the metadata needed to create HTTP requests. +type Request struct { + // body is a seekable reader over the request body payload. This is + // used to rewind the request data in between retries. + body io.ReadSeeker + + // Embed an HTTP request directly. This makes a *Request act exactly + // like an *http.Request so that all meta methods are supported. + *http.Request +} + +// NewRequest creates a new wrapped request. +func NewRequest(method, url string, body io.ReadSeeker) (*Request, error) { + // Wrap the body in a noop ReadCloser if non-nil. This prevents the + // reader from being closed by the HTTP client. + var rcBody io.ReadCloser + if body != nil { + rcBody = ioutil.NopCloser(body) + } + + // Make the request with the noop-closer for the body. + httpReq, err := http.NewRequest(method, url, rcBody) + if err != nil { + return nil, err + } + + // Check if we can set the Content-Length automatically. + if lr, ok := body.(LenReader); ok { + httpReq.ContentLength = int64(lr.Len()) + } + + return &Request{body, httpReq}, nil +} + +// RequestLogHook allows a function to run before each retry. The HTTP +// request which will be made, and the retry number (0 for the initial +// request) are available to users. The internal logger is exposed to +// consumers. +type RequestLogHook func(*log.Logger, *http.Request, int) + +// Client is used to make HTTP requests. It adds additional functionality +// like automatic retries to tolerate minor outages. +type Client struct { + HTTPClient *http.Client // Internal HTTP client. + Logger *log.Logger // Customer logger instance. + + RetryWaitMin time.Duration // Minimum time to wait + RetryWaitMax time.Duration // Maximum time to wait + RetryMax int // Maximum number of retries + + // RequestLogHook allows a user-supplied function to be called + // before each retry. + RequestLogHook RequestLogHook +} + +// NewClient creates a new Client with default settings. +func NewClient() *Client { + return &Client{ + HTTPClient: cleanhttp.DefaultClient(), + Logger: log.New(os.Stderr, "", log.LstdFlags), + RetryWaitMin: defaultRetryWaitMin, + RetryWaitMax: defaultRetryWaitMax, + RetryMax: defaultRetryMax, + } +} + +// Do wraps calling an HTTP method with retries. +func (c *Client) Do(req *Request) (*http.Response, error) { + c.Logger.Printf("[DEBUG] %s %s", req.Method, req.URL) + + for i := 0; ; i++ { + var code int // HTTP response code + + // Always rewind the request body when non-nil. + if req.body != nil { + if _, err := req.body.Seek(0, 0); err != nil { + return nil, fmt.Errorf("failed to seek body: %v", err) + } + } + + if c.RequestLogHook != nil { + c.RequestLogHook(c.Logger, req.Request, i) + } + + // Attempt the request + resp, err := c.HTTPClient.Do(req.Request) + if err != nil { + c.Logger.Printf("[ERR] %s %s request failed: %v", req.Method, req.URL, err) + goto RETRY + } + code = resp.StatusCode + + // Check the response code. We retry on 500-range responses to allow + // the server time to recover, as 500's are typically not permanent + // errors and may relate to outages on the server side. + if code%500 < 100 { + resp.Body.Close() + goto RETRY + } + return resp, nil + + RETRY: + remain := c.RetryMax - i + if remain == 0 { + break + } + wait := backoff(c.RetryWaitMin, c.RetryWaitMax, i) + desc := fmt.Sprintf("%s %s", req.Method, req.URL) + if code > 0 { + desc = fmt.Sprintf("%s (status: %d)", desc, code) + } + c.Logger.Printf("[DEBUG] %s: retrying in %s (%d left)", desc, wait, remain) + time.Sleep(wait) + } + + // Return an error if we fall out of the retry loop + return nil, fmt.Errorf("%s %s giving up after %d attempts", + req.Method, req.URL, c.RetryMax+1) +} + +// Get is a shortcut for doing a GET request without making a new client. +func Get(url string) (*http.Response, error) { + return defaultClient.Get(url) +} + +// Get is a convenience helper for doing simple GET requests. +func (c *Client) Get(url string) (*http.Response, error) { + req, err := NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + return c.Do(req) +} + +// Head is a shortcut for doing a HEAD request without making a new client. +func Head(url string) (*http.Response, error) { + return defaultClient.Head(url) +} + +// Head is a convenience method for doing simple HEAD requests. +func (c *Client) Head(url string) (*http.Response, error) { + req, err := NewRequest("HEAD", url, nil) + if err != nil { + return nil, err + } + return c.Do(req) +} + +// Post is a shortcut for doing a POST request without making a new client. +func Post(url, bodyType string, body io.ReadSeeker) (*http.Response, error) { + return defaultClient.Post(url, bodyType, body) +} + +// Post is a convenience method for doing simple POST requests. +func (c *Client) Post(url, bodyType string, body io.ReadSeeker) (*http.Response, error) { + req, err := NewRequest("POST", url, body) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", bodyType) + return c.Do(req) +} + +// PostForm is a shortcut to perform a POST with form data without creating +// a new client. +func PostForm(url string, data url.Values) (*http.Response, error) { + return defaultClient.PostForm(url, data) +} + +// PostForm is a convenience method for doing simple POST operations using +// pre-filled url.Values form data. +func (c *Client) PostForm(url string, data url.Values) (*http.Response, error) { + return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) +} + +// backoff is used to calculate how long to sleep before retrying +// after observing failures. It takes the minimum/maximum wait time and +// iteration, and returns the duration to wait. +func backoff(min, max time.Duration, iter int) time.Duration { + mult := math.Pow(2, float64(iter)) * float64(min) + sleep := time.Duration(mult) + if float64(sleep) != mult || sleep > max { + sleep = max + } + return sleep +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 26d6265e88..c208bca7de 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -442,6 +442,12 @@ "revision": "d30f09973e19c1dfcd120b2d9c4f168e68d6b5d5", "revisionTime": "2015-09-16T20:57:42Z" }, + { + "checksumSHA1": "9xZ1B0JppYRwKYtkT3PqQ255wqs=", + "path": "github.com/hashicorp/go-retryablehttp", + "revision": "0ef03300cde2dd0e2604ecc4e95ee7c76751be89", + "revisionTime": "2016-05-09T16:28:51Z" + }, { "checksumSHA1": "A1PcINvF3UiwHRKn8UcgARgvGRs=", "path": "github.com/hashicorp/go-rootcerts", From 1a2b13c2046e406caf7f136cdc362f8f7e8a0d29 Mon Sep 17 00:00:00 2001 From: Eric Herot Date: Thu, 7 Jul 2016 13:54:20 -0400 Subject: [PATCH 08/30] Pretty sure the method to delete a token role is not GET --- website/source/docs/auth/token.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/auth/token.html.md b/website/source/docs/auth/token.html.md index 97d360e9cd..f3bef2f2bd 100644 --- a/website/source/docs/auth/token.html.md +++ b/website/source/docs/auth/token.html.md @@ -486,7 +486,7 @@ of the header should be "X-Vault-Token" and the value should be the token.
    Method
    -
    GET
    +
    DELETE
    URL
    `/auth/token/roles/`
    From 35cbe22f5630f0f4848949a4087683d7aa8abdd8 Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Thu, 7 Jul 2016 17:57:36 -0400 Subject: [PATCH 09/30] Use go-uuid's GenerateUUID in PutWAL and discart logical.UUID() --- logical/framework/wal.go | 3 ++- logical/uuid.go | 21 --------------------- 2 files changed, 2 insertions(+), 22 deletions(-) delete mode 100644 logical/uuid.go diff --git a/logical/framework/wal.go b/logical/framework/wal.go index 6e6b234bce..1eaeb6779e 100644 --- a/logical/framework/wal.go +++ b/logical/framework/wal.go @@ -5,6 +5,7 @@ import ( "strings" "time" + "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/logical" ) @@ -43,7 +44,7 @@ func PutWAL(s logical.Storage, kind string, data interface{}) (string, error) { return "", err } - id, err := logical.UUID() + id, err := uuid.GenerateUUID() if err != nil { return "", err } diff --git a/logical/uuid.go b/logical/uuid.go deleted file mode 100644 index 25958da9cd..0000000000 --- a/logical/uuid.go +++ /dev/null @@ -1,21 +0,0 @@ -package logical - -import ( - "crypto/rand" - "fmt" - "time" -) - -// UUID returns a UUID. -func UUID() (string, error) { - unix := uint32(time.Now().UTC().Unix()) - - var b [12]byte - if _, err := rand.Read(b[:]); err != nil { - return "", err - } - - return fmt.Sprintf("%08x-%04x-%04x-%04x-%04x%08x", - unix, b[0:2], b[2:4], b[4:6], b[6:8], b[8:]), - nil -} From 9cfce6c3f32b9bcf23145596aff05feb6e0e9cf4 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 8 Jul 2016 05:05:46 +0000 Subject: [PATCH 10/30] Some policy concept page clarifications --- website/source/docs/concepts/policies.html.md | 43 +++---------------- 1 file changed, 6 insertions(+), 37 deletions(-) diff --git a/website/source/docs/concepts/policies.html.md b/website/source/docs/concepts/policies.html.md index eb1fe46576..75d03ce131 100644 --- a/website/source/docs/concepts/policies.html.md +++ b/website/source/docs/concepts/policies.html.md @@ -125,41 +125,10 @@ For tokens, they are associated at creation time with `vault token-create` and the `-policy` flags. Child tokens can be associated with a subset of a parent's policies. Root users can assign any policies. -There is no way to modify the policies associated with an active -identity. The identity must be revoked and reauthenticated to receive -the new policy list. - -If an _existing_ policy is modified, the modifications propagate -to all associated users instantly. The above paragraph is more specifically -stating that you can't add new or remove policies associated with an -active identity. - -## Changes from 0.1 - -In Vault versions prior to 0.2, the ACL policy language had a slightly -different specification and semantics. The current specification requires -that glob behavior explicitly be specified by adding the `*` character to -the end of a path. Previously, all paths were glob based matches and no -exact match could be specified. - -The other change is that deny had the lowest precedence. This meant if there -were two policies being merged (e.g. "ops" and "prod") and they had a conflicting -policy like: - -``` -path "sys/seal" { - policy = "deny" -} - -path "sys/seal" { - policy = "read" -} -``` - -The merge would previously give the "read" higher precedence. The current -version of Vault prioritizes the explicit deny, so that the "deny" would -take precedence. - -To make all Vault 0.1 policies compatible with Vault 0.2+, the explicit -glob character must be added to all the path prefixes. +There is no way to modify the policies associated with a token once the token +has been issued. The token must be revoked and a new one acquired to receive a +new set of policies. +However, the _contents_ of policies are parsed in real-time at every token use. +As a result, if a policy is modified, the modified rules will be in force the +next time a token with that policy attached is used to make a call to Vault. From b14497df952e7f175c3a9b8b3aa2443d219288c9 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 8 Jul 2016 10:34:49 -0400 Subject: [PATCH 11/30] Do some extra checking in the modified renewal check --- vault/expiration_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vault/expiration_test.go b/vault/expiration_test.go index c3940682ad..880161afe3 100644 --- a/vault/expiration_test.go +++ b/vault/expiration_test.go @@ -159,12 +159,12 @@ func TestExpiration_RegisterAuth_NoLease(t *testing.T) { // Should not be able to renew, no expiration resp, err := exp.RenewToken(&logical.Request{}, "auth/github/login", root.ID, 0) + if err != nil && (err != logical.ErrInvalidRequest || (resp != nil && resp.IsError() && resp.Error().Error() != "lease not found or lease is not renewable")) { + t.Fatalf("err: %v", err) + } if resp == nil { t.Fatal("expected a response") } - if err != logical.ErrInvalidRequest || resp.Error().Error() != "lease not found or lease is not renewable" { - t.Fatalf("err: %v", err) - } // Wait and check token is not invalidated time.Sleep(20 * time.Millisecond) From a1bbd24031adb4e874c7817108f650c835d68765 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 8 Jul 2016 10:41:11 -0400 Subject: [PATCH 12/30] Add documentation of retry env vars --- website/source/docs/commands/environment.html.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/website/source/docs/commands/environment.html.md b/website/source/docs/commands/environment.html.md index bbc9f48eba..d54e7cc679 100644 --- a/website/source/docs/commands/environment.html.md +++ b/website/source/docs/commands/environment.html.md @@ -46,6 +46,18 @@ The following table describes them: VAULT_CLIENT_KEY Path to an unencrypted PEM-encoded private key matching the client certificate. + + VAULT_RETRY_MAX + The maximum number of retries when a `5xx` error code is encountered. Default is `15`; set to `0` to disable retrying. + + + VAULT_RETRY_WAIT_MIN + The minimum amount of time to wait between retries when a `5xx` error code is encountered. Default is `1s`. + + + VAULT_RETRY_WAIT_MAX + The maximum amount of time to wait between retries when a `5xx` error code is encountered. Default is `30s`. + VAULT_SKIP_VERIFY If set, do not verify Vault's presented certificate before communicating with it. Setting this variable is not recommended except during testing. From 33453221322fdc174574dd34b252dcee617996ab Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 8 Jul 2016 10:41:14 -0400 Subject: [PATCH 13/30] changelog++ --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0110cf364a..4310ffa399 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ DEPRECATIONS/BREAKING CHANGES: * Issued certificates from the `pki` backend against new roles created or modified after upgrading will contain a set of default key usages. + * In the Go API, the function signature for `Request.ToHTTP()` has changed. FEATURES: @@ -18,6 +19,13 @@ FEATURES: modified after upgrading contain a set of default key usages for increased compatibility with OpenVPN and some other software. This set can be changed when writing a role definition. Existing roles are unaffected. [GH-1552] + * **Request Retrying in the CLI and Go API**: Requests that fail with a `5xx` + error code will now retry after a backoff. The minimum and maximum backoff + times, as well as the maximum total number of retries (including disabling + this functionality) can be set with environment variables. See the + [environment variable + documentation](https://www.vaultproject.io/docs/commands/environment.html) + for more details. [GH-1594] IMPROVEMENTS: * cli: Output formatting in the presence of warnings in the response object From f59a69bc5237e076c9809fbb59ae96e37fcad581 Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Thu, 7 Jul 2016 17:44:14 -0400 Subject: [PATCH 14/30] Remove Unix() invocations on 'time.Time' objects and removed conversion of time to UTC --- audit/format_json.go | 4 +- audit/hashstructure_test.go | 4 +- builtin/credential/aws-ec2/backend.go | 4 +- builtin/credential/aws-ec2/path_login.go | 4 +- .../aws-ec2/path_roletag_blacklist.go | 2 +- .../aws-ec2/path_tidy_identity_whitelist.go | 2 +- .../aws-ec2/path_tidy_roletag_blacklist.go | 2 +- builtin/logical/aws/secret_access_keys.go | 7 +-- builtin/logical/pki/backend_test.go | 8 +-- builtin/logical/pki/crl_util.go | 8 +-- builtin/logical/pki/path_fetch.go | 3 +- builtin/logical/pki/path_root.go | 4 +- .../logical/postgresql/path_role_create.go | 2 +- builtin/logical/transit/backend_test.go | 16 +++--- builtin/logical/transit/path_keys.go | 3 +- builtin/logical/transit/policy.go | 8 +-- http/sys_health.go | 16 +++--- http/sys_health_test.go | 18 +++--- logical/framework/backend.go | 6 +- logical/framework/backend_test.go | 2 +- logical/framework/lease.go | 4 +- logical/framework/lease_test.go | 2 +- logical/framework/wal.go | 4 +- logical/lease.go | 4 +- logical/lease_test.go | 2 +- vault/expiration.go | 12 ++-- vault/expiration_test.go | 13 +++-- vault/keyring_test.go | 6 +- vault/request_handling.go | 4 +- vault/token_store.go | 55 +++++++++++++------ vault/token_store_test.go | 55 ++++++++++--------- 31 files changed, 154 insertions(+), 130 deletions(-) diff --git a/audit/format_json.go b/audit/format_json.go index 2850ce2472..0e468068af 100644 --- a/audit/format_json.go +++ b/audit/format_json.go @@ -30,7 +30,7 @@ func (f *FormatJSON) FormatRequest( // Encode! enc := json.NewEncoder(w) return enc.Encode(&JSONRequestEntry{ - Time: time.Now().UTC().Format(time.RFC3339), + Time: time.Now().Format(time.RFC3339), Type: "request", Error: errString, @@ -100,7 +100,7 @@ func (f *FormatJSON) FormatResponse( // Encode! enc := json.NewEncoder(w) return enc.Encode(&JSONResponseEntry{ - Time: time.Now().UTC().Format(time.RFC3339), + Time: time.Now().Format(time.RFC3339), Type: "response", Error: errString, diff --git a/audit/hashstructure_test.go b/audit/hashstructure_test.go index 26b1d4153a..ab33acca20 100644 --- a/audit/hashstructure_test.go +++ b/audit/hashstructure_test.go @@ -18,7 +18,7 @@ func TestCopy_auth(t *testing.T) { expected := logical.Auth{ LeaseOptions: logical.LeaseOptions{ TTL: 1 * time.Hour, - IssueTime: time.Now().UTC(), + IssueTime: time.Now(), }, ClientToken: "foo", @@ -109,7 +109,7 @@ func TestHashString(t *testing.T) { } func TestHash(t *testing.T) { - now := time.Now().UTC() + now := time.Now() cases := []struct { Input interface{} diff --git a/builtin/credential/aws-ec2/backend.go b/builtin/credential/aws-ec2/backend.go index 16f151156e..5e63eac322 100644 --- a/builtin/credential/aws-ec2/backend.go +++ b/builtin/credential/aws-ec2/backend.go @@ -110,7 +110,7 @@ func Backend(conf *logical.BackendConfig) (*backend, error) { func (b *backend) periodicFunc(req *logical.Request) error { // Run the tidy operations for the first time. Then run it when current // time matches the nextTidyTime. - if b.nextTidyTime.IsZero() || !time.Now().UTC().Before(b.nextTidyTime) { + if b.nextTidyTime.IsZero() || !time.Now().Before(b.nextTidyTime) { // safety_buffer defaults to 180 days for roletag blacklist safety_buffer := 15552000 tidyBlacklistConfigEntry, err := b.lockedConfigTidyRoleTags(req.Storage) @@ -154,7 +154,7 @@ func (b *backend) periodicFunc(req *logical.Request) error { } // Update the time at which to run the tidy functions again. - b.nextTidyTime = time.Now().UTC().Add(b.tidyCooldownPeriod) + b.nextTidyTime = time.Now().Add(b.tidyCooldownPeriod) } return nil } diff --git a/builtin/credential/aws-ec2/path_login.go b/builtin/credential/aws-ec2/path_login.go index bab8495427..633a5bad9c 100644 --- a/builtin/credential/aws-ec2/path_login.go +++ b/builtin/credential/aws-ec2/path_login.go @@ -357,7 +357,7 @@ func (b *backend) pathLoginUpdate( } // Save the login attempt in the identity whitelist. - currentTime := time.Now().UTC() + currentTime := time.Now() if storedIdentity == nil { // Role, ClientNonce and CreationTime of the identity entry, // once set, should never change. @@ -550,7 +550,7 @@ func (b *backend) pathLoginRenew( } // Only LastUpdatedTime and ExpirationTime change and all other fields remain the same. - currentTime := time.Now().UTC() + currentTime := time.Now() storedIdentity.LastUpdatedTime = currentTime storedIdentity.ExpirationTime = currentTime.Add(longestMaxTTL) diff --git a/builtin/credential/aws-ec2/path_roletag_blacklist.go b/builtin/credential/aws-ec2/path_roletag_blacklist.go index 62ef923ead..e008d4494e 100644 --- a/builtin/credential/aws-ec2/path_roletag_blacklist.go +++ b/builtin/credential/aws-ec2/path_roletag_blacklist.go @@ -186,7 +186,7 @@ func (b *backend) pathRoletagBlacklistUpdate( blEntry = &roleTagBlacklistEntry{} } - currentTime := time.Now().UTC() + currentTime := time.Now() // Check if this is a creation of blacklist entry. if blEntry.CreationTime.IsZero() { diff --git a/builtin/credential/aws-ec2/path_tidy_identity_whitelist.go b/builtin/credential/aws-ec2/path_tidy_identity_whitelist.go index f16637dbf6..266d4596f2 100644 --- a/builtin/credential/aws-ec2/path_tidy_identity_whitelist.go +++ b/builtin/credential/aws-ec2/path_tidy_identity_whitelist.go @@ -65,7 +65,7 @@ func (b *backend) tidyWhitelistIdentity(s logical.Storage, safety_buffer int) er return err } - if time.Now().UTC().After(result.ExpirationTime.Add(bufferDuration)) { + if time.Now().After(result.ExpirationTime.Add(bufferDuration)) { if err := s.Delete("whitelist/identity" + instanceID); err != nil { return fmt.Errorf("error deleting identity of instanceID %s from storage: %s", instanceID, err) } diff --git a/builtin/credential/aws-ec2/path_tidy_roletag_blacklist.go b/builtin/credential/aws-ec2/path_tidy_roletag_blacklist.go index 307cfdc7fe..d163968ddb 100644 --- a/builtin/credential/aws-ec2/path_tidy_roletag_blacklist.go +++ b/builtin/credential/aws-ec2/path_tidy_roletag_blacklist.go @@ -64,7 +64,7 @@ func (b *backend) tidyBlacklistRoleTag(s logical.Storage, safety_buffer int) err return err } - if time.Now().UTC().After(result.ExpirationTime.Add(bufferDuration)) { + if time.Now().After(result.ExpirationTime.Add(bufferDuration)) { if err := s.Delete("blacklist/roletag" + tag); err != nil { return fmt.Errorf("error deleting tag %s from storage: %s", tag, err) } diff --git a/builtin/logical/aws/secret_access_keys.go b/builtin/logical/aws/secret_access_keys.go index 0c8c3da8ab..ef2e52e5d9 100644 --- a/builtin/logical/aws/secret_access_keys.go +++ b/builtin/logical/aws/secret_access_keys.go @@ -60,12 +60,7 @@ func genUsername(displayName, policyName, userType string) (ret string, warning // with, so don't insert display name or policy name at all } - ret = fmt.Sprintf( - "vault-%s%d-%d", - midString, - time.Now().Unix(), - rand.Int31n(10000)) - + ret = fmt.Sprintf("vault-%s%d-%d", midString, time.Now().Unix(), rand.Int31n(10000)) return } diff --git a/builtin/logical/pki/backend_test.go b/builtin/logical/pki/backend_test.go index 3c2602e78c..8f7cac62f8 100644 --- a/builtin/logical/pki/backend_test.go +++ b/builtin/logical/pki/backend_test.go @@ -958,7 +958,7 @@ func generateCATestingSteps(t *testing.T, caCert, caKey, otherCaCert string, int return fmt.Errorf("got an error: %s", resp.Data["error"].(string)) } - if resp.Data["revocation_time"].(int64) != 0 { + if !(resp.Data["revocation_time"].(time.Time)).IsZero() { return fmt.Errorf("expected a zero revocation time") } @@ -1115,7 +1115,7 @@ func generateCATestingSteps(t *testing.T, caCert, caKey, otherCaCert string, int return fmt.Errorf("got an error: %s", resp.Data["error"].(string)) } - if resp.Data["revocation_time"].(int64) != 0 { + if !(resp.Data["revocation_time"].(time.Time)).IsZero() { return fmt.Errorf("expected a zero revocation time") } @@ -1169,7 +1169,7 @@ func generateCATestingSteps(t *testing.T, caCert, caKey, otherCaCert string, int return fmt.Errorf("got an error: %s", resp.Data["error"].(string)) } - if resp.Data["revocation_time"].(int64) == 0 { + if (resp.Data["revocation_time"].(time.Time)).IsZero() { return fmt.Errorf("expected a non-zero revocation time") } @@ -1187,7 +1187,7 @@ func generateCATestingSteps(t *testing.T, caCert, caKey, otherCaCert string, int return fmt.Errorf("got an error: %s", resp.Data["error"].(string)) } - if resp.Data["revocation_time"].(int64) == 0 { + if (resp.Data["revocation_time"].(time.Time)).IsZero() { return fmt.Errorf("expected a non-zero revocation time") } diff --git a/builtin/logical/pki/crl_util.go b/builtin/logical/pki/crl_util.go index 2dd32b5f3a..0de2961508 100644 --- a/builtin/logical/pki/crl_util.go +++ b/builtin/logical/pki/crl_util.go @@ -12,8 +12,8 @@ import ( ) type revocationInfo struct { - CertificateBytes []byte `json:"certificate_bytes"` - RevocationTime int64 `json:"revocation_time"` + CertificateBytes []byte `json:"certificate_bytes"` + RevocationTime time.Time `json:"revocation_time"` } // Revokes a cert, and tries to be smart about error recovery @@ -87,7 +87,7 @@ func revokeCert(b *backend, req *logical.Request, serial string, fromLease bool) } revInfo.CertificateBytes = certEntry.Value - revInfo.RevocationTime = time.Now().Unix() + revInfo.RevocationTime = time.Now() certEntry, err = logical.StorageEntryJSON("revoked/"+serial, revInfo) if err != nil { @@ -153,7 +153,7 @@ func buildCRL(b *backend, req *logical.Request) error { revokedCerts = append(revokedCerts, pkix.RevokedCertificate{ SerialNumber: revokedCert.SerialNumber, - RevocationTime: time.Unix(revInfo.RevocationTime, 0), + RevocationTime: revInfo.RevocationTime, }) } diff --git a/builtin/logical/pki/path_fetch.go b/builtin/logical/pki/path_fetch.go index b37e9ff0fe..1616a204e5 100644 --- a/builtin/logical/pki/path_fetch.go +++ b/builtin/logical/pki/path_fetch.go @@ -3,6 +3,7 @@ package pki import ( "encoding/pem" "fmt" + "time" "github.com/hashicorp/vault/helper/certutil" "github.com/hashicorp/vault/logical" @@ -101,7 +102,7 @@ func (b *backend) pathFetchRead(req *logical.Request, data *framework.FieldData) var certEntry, revokedEntry *logical.StorageEntry var funcErr error var certificate []byte - var revocationTime int64 + var revocationTime time.Time response = &logical.Response{ Data: map[string]interface{}{}, } diff --git a/builtin/logical/pki/path_root.go b/builtin/logical/pki/path_root.go index b127533dc8..38de7970d1 100644 --- a/builtin/logical/pki/path_root.go +++ b/builtin/logical/pki/path_root.go @@ -98,7 +98,7 @@ func (b *backend) pathCAGenerateRoot( resp := &logical.Response{ Data: map[string]interface{}{ - "expiration": int64(parsedBundle.Certificate.NotAfter.Unix()), + "expiration": parsedBundle.Certificate.NotAfter, "serial_number": cb.SerialNumber, }, } @@ -234,7 +234,7 @@ func (b *backend) pathCASignIntermediate( resp := &logical.Response{ Data: map[string]interface{}{ - "expiration": int64(parsedBundle.Certificate.NotAfter.Unix()), + "expiration": parsedBundle.Certificate.NotAfter, "serial_number": cb.SerialNumber, }, } diff --git a/builtin/logical/postgresql/path_role_create.go b/builtin/logical/postgresql/path_role_create.go index 60ef442a5a..c5538c732b 100644 --- a/builtin/logical/postgresql/path_role_create.go +++ b/builtin/logical/postgresql/path_role_create.go @@ -77,7 +77,7 @@ func (b *backend) pathRoleCreateRead( if err != nil { return nil, err } - expiration := time.Now().UTC(). + expiration := time.Now(). Add(lease.Lease). Format("2006-01-02 15:04:05-0700") diff --git a/builtin/logical/transit/backend_test.go b/builtin/logical/transit/backend_test.go index 49296a0f9e..f42ea05d4a 100644 --- a/builtin/logical/transit/backend_test.go +++ b/builtin/logical/transit/backend_test.go @@ -222,14 +222,14 @@ func testAccStepReadPolicy(t *testing.T, name string, expectNone, derived bool) return nil } var d struct { - Name string `mapstructure:"name"` - Key []byte `mapstructure:"key"` - Keys map[string]int64 `mapstructure:"keys"` - CipherMode string `mapstructure:"cipher_mode"` - Derived bool `mapstructure:"derived"` - KDFMode string `mapstructure:"kdf_mode"` - DeletionAllowed bool `mapstructure:"deletion_allowed"` - ConvergentEncryption bool `mapstructure:"convergent_encryption"` + Name string `mapstructure:"name"` + Key []byte `mapstructure:"key"` + Keys map[string]time.Time `mapstructure:"keys"` + CipherMode string `mapstructure:"cipher_mode"` + Derived bool `mapstructure:"derived"` + KDFMode string `mapstructure:"kdf_mode"` + DeletionAllowed bool `mapstructure:"deletion_allowed"` + ConvergentEncryption bool `mapstructure:"convergent_encryption"` } if err := mapstructure.Decode(resp.Data, &d); err != nil { return err diff --git a/builtin/logical/transit/path_keys.go b/builtin/logical/transit/path_keys.go index accf00aa68..14ce59c4a4 100644 --- a/builtin/logical/transit/path_keys.go +++ b/builtin/logical/transit/path_keys.go @@ -3,6 +3,7 @@ package transit import ( "fmt" "strconv" + "time" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical/framework" @@ -109,7 +110,7 @@ func (b *backend) pathPolicyRead( resp.Data["convergent_encryption"] = p.ConvergentEncryption } - retKeys := map[string]int64{} + retKeys := map[string]time.Time{} for k, v := range p.Keys { retKeys[strconv.Itoa(k)] = v.CreationTime } diff --git a/builtin/logical/transit/policy.go b/builtin/logical/transit/policy.go index b9ede365c8..733e65b013 100644 --- a/builtin/logical/transit/policy.go +++ b/builtin/logical/transit/policy.go @@ -25,8 +25,8 @@ const ( // KeyEntry stores the key and metadata type KeyEntry struct { - Key []byte `json:"key"` - CreationTime int64 `json:"creation_time"` + Key []byte `json:"key"` + CreationTime time.Time `json:"creation_time"` } // KeyEntryMap is used to allow JSON marshal/unmarshal @@ -491,7 +491,7 @@ func (p *Policy) rotate(storage logical.Storage) error { p.Keys[p.LatestVersion] = KeyEntry{ Key: newKey, - CreationTime: time.Now().Unix(), + CreationTime: time.Now(), } // This ensures that with new key creations min decryption version is set @@ -510,7 +510,7 @@ func (p *Policy) migrateKeyToKeysMap() { p.Keys = KeyEntryMap{ 1: KeyEntry{ Key: p.Key, - CreationTime: time.Now().Unix(), + CreationTime: time.Now(), }, } p.Key = nil diff --git a/http/sys_health.go b/http/sys_health.go index 2883744c3c..04cb960454 100644 --- a/http/sys_health.go +++ b/http/sys_health.go @@ -115,17 +115,17 @@ func getSysHealth(core *vault.Core, r *http.Request) (int, *HealthResponse, erro // Format the body body := &HealthResponse{ - Initialized: init, - Sealed: sealed, - Standby: standby, - ServerTimeUTC: time.Now().UTC().Unix(), + Initialized: init, + Sealed: sealed, + Standby: standby, + ServerTime: time.Now(), } return code, body, nil } type HealthResponse struct { - Initialized bool `json:"initialized"` - Sealed bool `json:"sealed"` - Standby bool `json:"standby"` - ServerTimeUTC int64 `json:"server_time_utc"` + Initialized bool `json:"initialized"` + Sealed bool `json:"sealed"` + Standby bool `json:"standby"` + ServerTime time.Time `json:"server_time"` } diff --git a/http/sys_health_test.go b/http/sys_health_test.go index 452bd0cf1e..67abe75a18 100644 --- a/http/sys_health_test.go +++ b/http/sys_health_test.go @@ -29,9 +29,9 @@ func TestSysHealth_get(t *testing.T) { } testResponseStatus(t, resp, 200) testResponseBody(t, resp, &actual) - expected["server_time_utc"] = actual["server_time_utc"] + expected["server_time"] = actual["server_time"] if !reflect.DeepEqual(actual, expected) { - t.Fatalf("bad: %#v", actual) + t.Fatalf("bad: expected:%#v\nactual:%#v", expected, actual) } core.Seal(root) @@ -49,9 +49,9 @@ func TestSysHealth_get(t *testing.T) { } testResponseStatus(t, resp, 500) testResponseBody(t, resp, &actual) - expected["server_time_utc"] = actual["server_time_utc"] + expected["server_time"] = actual["server_time"] if !reflect.DeepEqual(actual, expected) { - t.Fatalf("bad: %#v", actual) + t.Fatalf("bad: expected:%#v\nactual:%#v", expected, actual) } } @@ -78,9 +78,9 @@ func TestSysHealth_customcodes(t *testing.T) { testResponseStatus(t, resp, 202) testResponseBody(t, resp, &actual) - expected["server_time_utc"] = actual["server_time_utc"] + expected["server_time"] = actual["server_time"] if !reflect.DeepEqual(actual, expected) { - t.Fatalf("bad: %#v", actual) + t.Fatalf("bad: expected:%#v\nactual:%#v", expected, actual) } core.Seal(root) @@ -102,9 +102,9 @@ func TestSysHealth_customcodes(t *testing.T) { } testResponseStatus(t, resp, 503) testResponseBody(t, resp, &actual) - expected["server_time_utc"] = actual["server_time_utc"] + expected["server_time"] = actual["server_time"] if !reflect.DeepEqual(actual, expected) { - t.Fatalf("bad: %#v", actual) + t.Fatalf("bad: expected:%#v\nactual:%#v", expected, actual) } } @@ -113,7 +113,7 @@ func TestSysHealth_head(t *testing.T) { ln, addr := TestServer(t, core) defer ln.Close() - testData := []struct{ + testData := []struct { uri string code int }{ diff --git a/logical/framework/backend.go b/logical/framework/backend.go index 9f274fd22d..dda2a05d0d 100644 --- a/logical/framework/backend.go +++ b/logical/framework/backend.go @@ -450,9 +450,9 @@ func (b *Backend) handleWALRollback( if age == 0 { age = 10 * time.Minute } - minAge := time.Now().UTC().Add(-1 * age) + minAge := time.Now().Add(-1 * age) if _, ok := req.Data["immediate"]; ok { - minAge = time.Now().UTC().Add(1000 * time.Hour) + minAge = time.Now().Add(1000 * time.Hour) } for _, k := range keys { @@ -466,7 +466,7 @@ func (b *Backend) handleWALRollback( } // If the entry isn't old enough, then don't roll it back - if !time.Unix(entry.CreatedAt, 0).Before(minAge) { + if !entry.CreatedAt.Before(minAge) { continue } diff --git a/logical/framework/backend_test.go b/logical/framework/backend_test.go index a3b34d7ca3..d777ecda8a 100644 --- a/logical/framework/backend_test.go +++ b/logical/framework/backend_test.go @@ -263,7 +263,7 @@ func TestBackendHandleRequest_renewExtend(t *testing.T) { } req := logical.RenewRequest("/foo", secret.Response(nil, nil).Secret, nil) - req.Secret.IssueTime = time.Now().UTC() + req.Secret.IssueTime = time.Now() req.Secret.Increment = 1 * time.Hour resp, err := b.HandleRequest(req) if err != nil { diff --git a/logical/framework/lease.go b/logical/framework/lease.go index 3e5ebe154d..4fd2ac902c 100644 --- a/logical/framework/lease.go +++ b/logical/framework/lease.go @@ -45,10 +45,10 @@ func LeaseExtend(backendIncrement, backendMax time.Duration, systemView logical. } // We cannot go past this time - maxValidTime := leaseOpts.IssueTime.UTC().Add(max) + maxValidTime := leaseOpts.IssueTime.Add(max) // Get the current time - now := time.Now().UTC() + now := time.Now() // If we are past the max TTL, we shouldn't be in this function...but // fast path out if we are diff --git a/logical/framework/lease_test.go b/logical/framework/lease_test.go index 0ab036a61a..b45b9c7164 100644 --- a/logical/framework/lease_test.go +++ b/logical/framework/lease_test.go @@ -14,7 +14,7 @@ func TestLeaseExtend(t *testing.T) { MaxLeaseTTLVal: 30 * time.Hour, } - now := time.Now().UTC().Round(time.Hour) + now := time.Now().Round(time.Hour) cases := map[string]struct { BackendDefault time.Duration diff --git a/logical/framework/wal.go b/logical/framework/wal.go index 6e6b234bce..306fecbde2 100644 --- a/logical/framework/wal.go +++ b/logical/framework/wal.go @@ -15,7 +15,7 @@ type WALEntry struct { ID string `json:"-"` Kind string `json:"type"` Data interface{} `json:"data"` - CreatedAt int64 `json:"created_at"` + CreatedAt time.Time `json:"created_at"` } // PutWAL writes some data to the WAL. @@ -37,7 +37,7 @@ func PutWAL(s logical.Storage, kind string, data interface{}) (string, error) { value, err := json.Marshal(&WALEntry{ Kind: kind, Data: data, - CreatedAt: time.Now().UTC().Unix(), + CreatedAt: time.Now(), }) if err != nil { return "", err diff --git a/logical/lease.go b/logical/lease.go index 871954457d..ed0b26b51c 100644 --- a/logical/lease.go +++ b/logical/lease.go @@ -20,7 +20,7 @@ type LeaseOptions struct { // IssueTime is the time of issue for the original lease. This is // only available on a Renew operation and has no effect when returning // a response. It can be used to enforce maximum lease periods by - // a logical backend. This time will always be in UTC. + // a logical backend. IssueTime time.Time `json:"-"` } @@ -42,7 +42,7 @@ func (l *LeaseOptions) LeaseTotal() time.Duration { func (l *LeaseOptions) ExpirationTime() time.Time { var expireTime time.Time if l.LeaseEnabled() { - expireTime = time.Now().UTC().Add(l.LeaseTotal()) + expireTime = time.Now().Add(l.LeaseTotal()) } return expireTime } diff --git a/logical/lease_test.go b/logical/lease_test.go index 9b9ec6a56b..050b7db8e9 100644 --- a/logical/lease_test.go +++ b/logical/lease_test.go @@ -41,7 +41,7 @@ func TestLeaseOptionsExpirationTime(t *testing.T) { var l LeaseOptions l.TTL = 1 * time.Hour - limit := time.Now().UTC().Add(time.Hour) + limit := time.Now().Add(time.Hour) exp := l.ExpirationTime() if exp.Before(limit) { t.Fatalf("bad: %s", exp) diff --git a/vault/expiration.go b/vault/expiration.go index 0b56135bad..30a73e8b67 100644 --- a/vault/expiration.go +++ b/vault/expiration.go @@ -141,7 +141,7 @@ func (m *ExpirationManager) Restore() error { } // Determine the remaining time to expiration - expires := le.ExpireTime.Sub(time.Now().UTC()) + expires := le.ExpireTime.Sub(time.Now()) if expires <= 0 { expires = minRevokeDelay } @@ -334,7 +334,7 @@ func (m *ExpirationManager) Renew(leaseID string, increment time.Duration) (*log le.Data = resp.Data le.Secret = resp.Secret le.ExpireTime = resp.Secret.ExpirationTime() - le.LastRenewalTime = time.Now().UTC() + le.LastRenewalTime = time.Now() if err := m.persistEntry(le); err != nil { return nil, err } @@ -395,7 +395,7 @@ func (m *ExpirationManager) RenewToken(req *logical.Request, source string, toke // Update the lease entry le.Auth = resp.Auth le.ExpireTime = resp.Auth.ExpirationTime() - le.LastRenewalTime = time.Now().UTC() + le.LastRenewalTime = time.Now() if err := m.persistEntry(le); err != nil { return nil, err } @@ -433,7 +433,7 @@ func (m *ExpirationManager) Register(req *logical.Request, resp *logical.Respons Path: req.Path, Data: resp.Data, Secret: resp.Secret, - IssueTime: time.Now().UTC(), + IssueTime: time.Now(), ExpireTime: resp.Secret.ExpirationTime(), } @@ -466,7 +466,7 @@ func (m *ExpirationManager) RegisterAuth(source string, auth *logical.Auth) erro ClientToken: auth.ClientToken, Auth: auth, Path: source, - IssueTime: time.Now().UTC(), + IssueTime: time.Now(), ExpireTime: auth.ExpirationTime(), } @@ -762,7 +762,7 @@ func (le *leaseEntry) renewable() error { } // Determine if the lease is expired - if le.ExpireTime.Before(time.Now().UTC()) { + if le.ExpireTime.Before(time.Now()) { return fmt.Errorf("lease expired") } diff --git a/vault/expiration_test.go b/vault/expiration_test.go index c5a4f8004b..44e3c7daa2 100644 --- a/vault/expiration_test.go +++ b/vault/expiration_test.go @@ -899,9 +899,9 @@ func TestExpiration_PersistLoadDelete(t *testing.T) { TTL: time.Minute, }, }, - IssueTime: time.Now().UTC(), - ExpireTime: time.Now().UTC(), - LastRenewalTime: time.Time{}.UTC(), + IssueTime: time.Now(), + ExpireTime: time.Now(), + LastRenewalTime: time.Time{}, } if err := exp.persistEntry(le); err != nil { t.Fatalf("err: %v", err) @@ -911,8 +911,9 @@ func TestExpiration_PersistLoadDelete(t *testing.T) { if err != nil { t.Fatalf("err: %v", err) } + le.LastRenewalTime = out.LastRenewalTime if !reflect.DeepEqual(out, le) { - t.Fatalf("\nout: %#v\nexpect: %#v\n", out, le) + t.Fatalf("bad: expected:%#v\nactual:%#v", le, out) } err = exp.deleteEntry("foo/bar/1234") @@ -941,8 +942,8 @@ func TestLeaseEntry(t *testing.T) { TTL: time.Minute, }, }, - IssueTime: time.Now().UTC(), - ExpireTime: time.Now().UTC(), + IssueTime: time.Now(), + ExpireTime: time.Now(), } enc, err := le.encode() diff --git a/vault/keyring_test.go b/vault/keyring_test.go index e7b369e4ab..5fc482797f 100644 --- a/vault/keyring_test.go +++ b/vault/keyring_test.go @@ -140,8 +140,8 @@ func TestKeyring_Serialize(t *testing.T) { testKey := []byte("testing") testSecond := []byte("second") - k, _ = k.AddKey(&Key{Term: 1, Version: 1, Value: testKey, InstallTime: time.Now().UTC()}) - k, _ = k.AddKey(&Key{Term: 2, Version: 1, Value: testSecond, InstallTime: time.Now().UTC()}) + k, _ = k.AddKey(&Key{Term: 1, Version: 1, Value: testKey, InstallTime: time.Now()}) + k, _ = k.AddKey(&Key{Term: 2, Version: 1, Value: testSecond, InstallTime: time.Now()}) buf, err := k.Serialize() if err != nil { @@ -177,7 +177,7 @@ func TestKey_Serialize(t *testing.T) { Term: 10, Version: 1, Value: []byte("foobarbaz"), - InstallTime: time.Now().UTC(), + InstallTime: time.Now(), } buf, err := k.Serialize() diff --git a/vault/request_handling.go b/vault/request_handling.go index ae2021e031..2191c7483f 100644 --- a/vault/request_handling.go +++ b/vault/request_handling.go @@ -327,7 +327,7 @@ func (c *Core) handleLoginRequest(req *logical.Request) (*logical.Response, *log Policies: auth.Policies, Meta: auth.Metadata, DisplayName: auth.DisplayName, - CreationTime: time.Now().Unix(), + CreationTime: time.Now(), TTL: auth.TTL, } @@ -389,7 +389,7 @@ func (c *Core) wrapInCubbyhole(req *logical.Request, resp *logical.Response) (*l te := TokenEntry{ Path: req.Path, Policies: []string{"response-wrapping"}, - CreationTime: creationTime.Unix(), + CreationTime: creationTime, TTL: resp.WrapInfo.TTL, NumUses: 1, ExplicitMaxTTL: resp.WrapInfo.TTL, diff --git a/vault/token_store.go b/vault/token_store.go index 685285a92d..a3253fc27d 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -414,18 +414,41 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) // TokenEntry is used to represent a given token type TokenEntry struct { - ID string // ID of this entry, generally a random UUID - Accessor string // Accessor for this token, a random UUID - Parent string // Parent token, used for revocation trees - Policies []string // Which named policies should be used - Path string // Used for audit trails, this is something like "auth/user/login" - Meta map[string]string // Used for auditing. This could include things like "source", "user", "ip" - DisplayName string // Used for operators to be able to associate with the source - NumUses int // Used to restrict the number of uses (zero is unlimited). This is to support one-time-tokens (generalized). - CreationTime int64 // Time of token creation - TTL time.Duration // Duration set when token was created - ExplicitMaxTTL time.Duration // Explicit maximum TTL on the token - Role string // If set, the role that was used for parameters at creation time + // ID of this entry, generally a random UUID + ID string `json:"id" mapstructure:"id" structs:"id"` + + // Accessor for this token, a random UUID + Accessor string `json:"accessor" mapstructure:"accessor" structs:"accessor"` + + // Parent token, used for revocation trees + Parent string `json:"parent" mapstructure:"parent" structs:"parent"` + + // Which named policies should be used + Policies []string `json:"policies" mapstructure:"policies" structs:"policies"` + + // Used for audit trails, this is something like "auth/user/login" + Path string `json:"path" mapstructure:"path" structs:"path"` + + // Used for auditing. This could include things like "source", "user", "ip" + Meta map[string]string `json:"meta" mapstructure:"meta" structs:"meta"` + + // Used for operators to be able to associate with the source + DisplayName string `json:"display_name" mapstructure:"display_name" structs:"display_name"` + + // Used to restrict the number of uses (zero is unlimited). This is to support one-time-tokens (generalized). + NumUses int `json:"num_uses" mapstructure:"num_uses" structs:"num_uses"` + + // Time of token creation + CreationTime time.Time `json:"creation_time" mapstructure:"creation_time" structs:"creation_time"` + + // Duration set when token was created + TTL time.Duration `json:"ttl" mapstructure:"ttl" structs:"ttl"` + + // Explicit maximum TTL on the token + ExplicitMaxTTL time.Duration `json:"" mapstructure:"" structs:""` + + // If set, the role that was used for parameters at creation time + Role string `json:"role" mapstructure:"role" structs:"role"` } // tsRoleEntry contains token store role information @@ -474,7 +497,7 @@ func (ts *TokenStore) rootToken() (*TokenEntry, error) { Policies: []string{"root"}, Path: "auth/token/root", DisplayName: "root", - CreationTime: time.Now().Unix(), + CreationTime: time.Now(), } if err := ts.create(te); err != nil { return nil, err @@ -970,7 +993,7 @@ func (ts *TokenStore) handleCreateCommon( Meta: data.Metadata, DisplayName: "token", NumUses: data.NumUses, - CreationTime: time.Now().Unix(), + CreationTime: time.Now(), } renewable := true @@ -1306,7 +1329,7 @@ func (ts *TokenStore) handleLookup( "display_name": out.DisplayName, "num_uses": out.NumUses, "orphan": false, - "creation_time": int64(out.CreationTime), + "creation_time": out.CreationTime, "creation_ttl": int64(out.TTL.Seconds()), "ttl": int64(0), "role": out.Role, @@ -1325,7 +1348,7 @@ func (ts *TokenStore) handleLookup( } if leaseTimes != nil { if !leaseTimes.LastRenewalTime.IsZero() { - resp.Data["last_renewal_time"] = leaseTimes.LastRenewalTime.Unix() + resp.Data["last_renewal_time"] = leaseTimes.LastRenewalTime } if !leaseTimes.ExpireTime.IsZero() { resp.Data["ttl"] = int64(leaseTimes.ExpireTime.Sub(time.Now().Round(time.Second)).Seconds()) diff --git a/vault/token_store_test.go b/vault/token_store_test.go index 0c093fdf62..dc85cc68a7 100644 --- a/vault/token_store_test.go +++ b/vault/token_store_test.go @@ -156,7 +156,7 @@ func TestTokenStore_RootToken(t *testing.T) { t.Fatalf("err: %v", err) } if !reflect.DeepEqual(out, te) { - t.Fatalf("bad: %#v", out) + t.Fatalf("bad: expected:%#v\nactual:%#v", te, out) } } @@ -175,8 +175,9 @@ func TestTokenStore_CreateLookup(t *testing.T) { if err != nil { t.Fatalf("err: %v", err) } + ent.CreationTime = out.CreationTime if !reflect.DeepEqual(out, ent) { - t.Fatalf("bad: %#v", out) + t.Fatalf("bad: expected:%#v\nactual:%#v", ent, out) } // New store should share the salt @@ -191,7 +192,7 @@ func TestTokenStore_CreateLookup(t *testing.T) { t.Fatalf("err: %v", err) } if !reflect.DeepEqual(out, ent) { - t.Fatalf("bad: %#v", out) + t.Fatalf("bad: expected:%#v\nactual:%#v", ent, out) } } @@ -207,15 +208,16 @@ func TestTokenStore_CreateLookup_ProvidedID(t *testing.T) { t.Fatalf("err: %v", err) } if ent.ID != "foobarbaz" { - t.Fatalf("bad: %#v", ent) + t.Fatalf("bad: ent.ID: expected:\"foobarbaz\"\n actual:%s", ent.ID) } out, err := ts.Lookup(ent.ID) if err != nil { t.Fatalf("err: %v", err) } + ent.CreationTime = out.CreationTime if !reflect.DeepEqual(out, ent) { - t.Fatalf("bad: %#v", out) + t.Fatalf("bad: expected:%#v\nactual:%#v", ent, out) } // New store should share the salt @@ -230,7 +232,7 @@ func TestTokenStore_CreateLookup_ProvidedID(t *testing.T) { t.Fatalf("err: %v", err) } if !reflect.DeepEqual(out, ent) { - t.Fatalf("bad: %#v", out) + t.Fatalf("bad: expected:%#v\nactual:%#v", ent, out) } } @@ -259,7 +261,7 @@ func TestTokenStore_UseToken(t *testing.T) { } if !reflect.DeepEqual(ent, ent2) { - t.Fatalf("bad: %#v %#v", ent, ent2) + t.Fatalf("bad: ent:%#v ent2:%#v", ent, ent2) } // Create a retstricted token @@ -411,8 +413,9 @@ func TestTokenStore_Revoke_Orphan(t *testing.T) { if err != nil { t.Fatalf("err: %v", err) } + ent2.CreationTime = out.CreationTime if !reflect.DeepEqual(out, ent2) { - t.Fatalf("bad: %#v", out) + t.Fatalf("bad: expected:%#v\nactual:%#v", ent2, out) } } @@ -530,7 +533,7 @@ func TestTokenStore_HandleRequest_CreateToken_DisplayName(t *testing.T) { } expected.CreationTime = out.CreationTime if !reflect.DeepEqual(out, expected) { - t.Fatalf("bad:\ngot:\n%#v\nexpected:\n%#v\n", out, expected) + t.Fatalf("bad: expected:%#v\nactual:%#v", expected, out) } } @@ -562,7 +565,7 @@ func TestTokenStore_HandleRequest_CreateToken_NumUses(t *testing.T) { } expected.CreationTime = out.CreationTime if !reflect.DeepEqual(out, expected) { - t.Fatalf("bad: %#v", out) + t.Fatalf("bad: expected:%#v\nactual:%#v", expected, out) } } @@ -625,7 +628,7 @@ func TestTokenStore_HandleRequest_CreateToken_NoPolicy(t *testing.T) { } expected.CreationTime = out.CreationTime if !reflect.DeepEqual(out, expected) { - t.Fatalf("bad: %#v", out) + t.Fatalf("bad: expected:%#v\nactual:%#v", expected, out) } } @@ -812,7 +815,7 @@ func TestTokenStore_HandleRequest_CreateToken_Metadata(t *testing.T) { out, _ := ts.Lookup(resp.Auth.ClientToken) if !reflect.DeepEqual(out.Meta, meta) { - t.Fatalf("bad: %#v", out) + t.Fatalf("bad: expected:%#v\nactual:%#v", meta, out.Meta) } } @@ -982,13 +985,13 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) { "explicit_max_ttl": int64(0), } - if resp.Data["creation_time"].(int64) == 0 { + if (resp.Data["creation_time"].(time.Time)).IsZero() { t.Fatalf("creation time was zero") } delete(resp.Data, "creation_time") if !reflect.DeepEqual(resp.Data, exp) { - t.Fatalf("bad:\n%#v\nexp:\n%#v\n", resp.Data, exp) + t.Fatalf("bad: expected:%#v\nactual:%#v", exp, resp.Data) } testCoreMakeToken(t, c, root, "client", "3600s", []string{"foo"}) @@ -1019,7 +1022,7 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) { "renewable": true, } - if resp.Data["creation_time"].(int64) == 0 { + if (resp.Data["creation_time"].(time.Time)).IsZero() { t.Fatalf("creation time was zero") } delete(resp.Data, "creation_time") @@ -1030,7 +1033,7 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) { } if !reflect.DeepEqual(resp.Data, exp) { - t.Fatalf("bad:\n%#v\nexp:\n%#v\n", resp.Data, exp) + t.Fatalf("bad: expected:%#v\nactual:%#v", exp, resp.Data) } // Test via POST @@ -1062,7 +1065,7 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) { "renewable": true, } - if resp.Data["creation_time"].(int64) == 0 { + if (resp.Data["creation_time"].(time.Time)).IsZero() { t.Fatalf("creation time was zero") } delete(resp.Data, "creation_time") @@ -1073,7 +1076,7 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) { } if !reflect.DeepEqual(resp.Data, exp) { - t.Fatalf("bad:\n%#v\nexp:\n%#v\n", resp.Data, exp) + t.Fatalf("bad: expected:%#v\nactual:%#v", exp, resp.Data) } // Test last_renewal_time functionality @@ -1095,7 +1098,7 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) { t.Fatalf("bad: %#v", resp) } - if resp.Data["last_renewal_time"].(int64) == 0 { + if (resp.Data["last_renewal_time"].(time.Time)).IsZero() { t.Fatalf("last_renewal_time was zero") } } @@ -1127,13 +1130,13 @@ func TestTokenStore_HandleRequest_LookupSelf(t *testing.T) { "explicit_max_ttl": int64(0), } - if resp.Data["creation_time"].(int64) == 0 { + if (resp.Data["creation_time"].(time.Time)).IsZero() { t.Fatalf("creation time was zero") } delete(resp.Data, "creation_time") if !reflect.DeepEqual(resp.Data, exp) { - t.Fatalf("bad:\ngot %#v\nexpected: %#v\n", resp.Data, exp) + t.Fatalf("bad: expected:%#v\nactual:%#v", exp, resp.Data) } } @@ -1163,7 +1166,7 @@ func TestTokenStore_HandleRequest_Renew(t *testing.T) { // Get the original expire time to compare originalExpire := auth.ExpirationTime() - beforeRenew := time.Now().UTC() + beforeRenew := time.Now() req := logical.TestRequest(t, logical.UpdateOperation, "renew/"+root.ID) req.Data["increment"] = "3600s" resp, err := ts.HandleRequest(req) @@ -1207,7 +1210,7 @@ func TestTokenStore_HandleRequest_RenewSelf(t *testing.T) { // Get the original expire time to compare originalExpire := auth.ExpirationTime() - beforeRenew := time.Now().UTC() + beforeRenew := time.Now() req := logical.TestRequest(t, logical.UpdateOperation, "renew-self") req.ClientToken = auth.ClientToken req.Data["increment"] = "3600s" @@ -1279,7 +1282,7 @@ func TestTokenStore_RoleCRUD(t *testing.T) { } if !reflect.DeepEqual(expected, resp.Data) { - t.Fatalf("expected:\n%v\nactual:\n%v\n", expected, resp.Data) + t.Fatalf("bad: expected:%#v\nactual:%#v", expected, resp.Data) } // Now test updating; this should be set to an UpdateOperation @@ -1322,7 +1325,7 @@ func TestTokenStore_RoleCRUD(t *testing.T) { } if !reflect.DeepEqual(expected, resp.Data) { - t.Fatalf("expected:\n%v\nactual:\n%v\n", expected, resp.Data) + t.Fatalf("bad: expected:%#v\nactual:%#v", expected, resp.Data) } // Now test setting explicit max ttl at the same time as period, which @@ -1370,7 +1373,7 @@ func TestTokenStore_RoleCRUD(t *testing.T) { } if !reflect.DeepEqual(expected, resp.Data) { - t.Fatalf("expected:\n%v\nactual:\n%v\n", expected, resp.Data) + t.Fatalf("bad: expected:%#v\nactual:%#v", expected, resp.Data) } req.Operation = logical.ListOperation From d5b73fa0f51eeff3a8cb005845c7dde2a38e73ed Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Fri, 8 Jul 2016 19:01:36 -0400 Subject: [PATCH 15/30] Place error check before the response check in expiration test --- vault/expiration_test.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/vault/expiration_test.go b/vault/expiration_test.go index 880161afe3..1ed21eb457 100644 --- a/vault/expiration_test.go +++ b/vault/expiration_test.go @@ -160,7 +160,7 @@ func TestExpiration_RegisterAuth_NoLease(t *testing.T) { // Should not be able to renew, no expiration resp, err := exp.RenewToken(&logical.Request{}, "auth/github/login", root.ID, 0) if err != nil && (err != logical.ErrInvalidRequest || (resp != nil && resp.IsError() && resp.Error().Error() != "lease not found or lease is not renewable")) { - t.Fatalf("err: %v", err) + t.Fatalf("bad: err:%v resp:%#v", err, resp) } if resp == nil { t.Fatal("expected a response") @@ -459,12 +459,13 @@ func TestExpiration_RenewToken_NotRenewable(t *testing.T) { // Attempt to renew the token resp, err := exp.RenewToken(&logical.Request{}, "auth/github/login", root.ID, 0) + if err != nil && (err != logical.ErrInvalidRequest || (resp != nil && resp.IsError() && resp.Error().Error() != "lease not found or lease is not renewable")) { + t.Fatalf("bad: err:%v resp:%#v", err, resp) + } if resp == nil { t.Fatal("expected a response") } - if err != logical.ErrInvalidRequest || resp.Error().Error() != "lease is not renewable" { - t.Fatalf("err: %v", err) - } + } func TestExpiration_Renew(t *testing.T) { From fa4a00f65b847f8edd515265424af89e5374367f Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Fri, 8 Jul 2016 20:30:27 -0400 Subject: [PATCH 16/30] Fix broken build --- vault/expiration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vault/expiration_test.go b/vault/expiration_test.go index 1ed21eb457..dd4337f5d0 100644 --- a/vault/expiration_test.go +++ b/vault/expiration_test.go @@ -159,7 +159,7 @@ func TestExpiration_RegisterAuth_NoLease(t *testing.T) { // Should not be able to renew, no expiration resp, err := exp.RenewToken(&logical.Request{}, "auth/github/login", root.ID, 0) - if err != nil && (err != logical.ErrInvalidRequest || (resp != nil && resp.IsError() && resp.Error().Error() != "lease not found or lease is not renewable")) { + if err != nil && (err != logical.ErrInvalidRequest || (resp != nil && resp.IsError() && resp.Error().Error() != "lease is not renewable")) { t.Fatalf("bad: err:%v resp:%#v", err, resp) } if resp == nil { From 24b89dd452a2ebe8519616ea7510a04117eb6850 Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Fri, 8 Jul 2016 23:16:58 -0400 Subject: [PATCH 17/30] Fix broken build --- vault/expiration_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vault/expiration_test.go b/vault/expiration_test.go index dd4337f5d0..1384da0195 100644 --- a/vault/expiration_test.go +++ b/vault/expiration_test.go @@ -159,7 +159,7 @@ func TestExpiration_RegisterAuth_NoLease(t *testing.T) { // Should not be able to renew, no expiration resp, err := exp.RenewToken(&logical.Request{}, "auth/github/login", root.ID, 0) - if err != nil && (err != logical.ErrInvalidRequest || (resp != nil && resp.IsError() && resp.Error().Error() != "lease is not renewable")) { + if err != nil && (err != logical.ErrInvalidRequest || (resp != nil && resp.IsError() && resp.Error().Error() != "lease not found or lease is not renewable")) { t.Fatalf("bad: err:%v resp:%#v", err, resp) } if resp == nil { @@ -459,7 +459,7 @@ func TestExpiration_RenewToken_NotRenewable(t *testing.T) { // Attempt to renew the token resp, err := exp.RenewToken(&logical.Request{}, "auth/github/login", root.ID, 0) - if err != nil && (err != logical.ErrInvalidRequest || (resp != nil && resp.IsError() && resp.Error().Error() != "lease not found or lease is not renewable")) { + if err != nil && (err != logical.ErrInvalidRequest || (resp != nil && resp.IsError() && resp.Error().Error() != "lease is not renewable")) { t.Fatalf("bad: err:%v resp:%#v", err, resp) } if resp == nil { From 984641af213529bd83faefab2311f70c58995e87 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Mon, 11 Jul 2016 17:46:23 +0000 Subject: [PATCH 18/30] Factor out parsing duration second type and use it for parsing tune values too --- helper/duration/duration.go | 28 ++++++++++++++++++++++++++++ helper/duration/duration_test.go | 20 ++++++++++++++++++++ logical/framework/field_data.go | 22 +++++----------------- vault/logical_system.go | 19 +++++++++++-------- 4 files changed, 64 insertions(+), 25 deletions(-) create mode 100644 helper/duration/duration.go create mode 100644 helper/duration/duration_test.go diff --git a/helper/duration/duration.go b/helper/duration/duration.go new file mode 100644 index 0000000000..e3c5b683c8 --- /dev/null +++ b/helper/duration/duration.go @@ -0,0 +1,28 @@ +package duration + +import ( + "strconv" + "strings" + "time" +) + +func ParseDurationSecond(inp string) (int, error) { + var result int + // Look for a suffix otherwise its a plain second value + if strings.HasSuffix(inp, "s") || strings.HasSuffix(inp, "m") || strings.HasSuffix(inp, "h") { + dur, err := time.ParseDuration(inp) + if err != nil { + return result, err + } + result = int(dur.Seconds()) + } else { + // Plain integer + val, err := strconv.ParseInt(inp, 10, 64) + if err != nil { + return result, err + } + result = int(val) + } + + return result, nil +} diff --git a/helper/duration/duration_test.go b/helper/duration/duration_test.go new file mode 100644 index 0000000000..9d3124ff45 --- /dev/null +++ b/helper/duration/duration_test.go @@ -0,0 +1,20 @@ +package duration + +import "testing" + +func Test_ParseDurationSecond(t *testing.T) { + outp, err := ParseDurationSecond("9876s") + if err != nil { + t.Fatal(err) + } + if outp != 9876 { + t.Fatal("not equivalent") + } + outp, err = ParseDurationSecond("9876") + if err != nil { + t.Fatal(err) + } + if outp != 9876 { + t.Fatal("not equivalent") + } +} diff --git a/logical/framework/field_data.go b/logical/framework/field_data.go index 4edeac1e90..00e0f3122a 100644 --- a/logical/framework/field_data.go +++ b/logical/framework/field_data.go @@ -2,10 +2,8 @@ package framework import ( "fmt" - "strconv" - "strings" - "time" + "github.com/hashicorp/vault/helper/duration" "github.com/mitchellh/mapstructure" ) @@ -152,6 +150,7 @@ func (d *FieldData) getPrimitive( case TypeDurationSecond: var result int + var err error switch inp := raw.(type) { case nil: return nil, false, nil @@ -162,20 +161,9 @@ func (d *FieldData) getPrimitive( case float64: result = int(inp) case string: - // Look for a suffix otherwise its a plain second value - if strings.HasSuffix(inp, "s") || strings.HasSuffix(inp, "m") || strings.HasSuffix(inp, "h") { - dur, err := time.ParseDuration(inp) - if err != nil { - return nil, true, err - } - result = int(dur.Seconds()) - } else { - // Plain integer - val, err := strconv.ParseInt(inp, 10, 64) - if err != nil { - return nil, true, err - } - result = int(val) + result, err = duration.ParseDurationSecond(inp) + if err != nil { + return nil, true, err } default: diff --git a/vault/logical_system.go b/vault/logical_system.go index 1263d2c2da..c3caaad8df 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -6,6 +6,7 @@ import ( "sync" "time" + "github.com/hashicorp/vault/helper/duration" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical/framework" "github.com/mitchellh/mapstructure" @@ -702,26 +703,26 @@ func (b *SystemBackend) handleMount( case "": case "system": default: - tmpDef, err := time.ParseDuration(apiConfig.DefaultLeaseTTL) + tmpDef, err := duration.ParseDurationSecond(apiConfig.DefaultLeaseTTL) if err != nil { return logical.ErrorResponse(fmt.Sprintf( "unable to parse default TTL of %s: %s", apiConfig.DefaultLeaseTTL, err)), logical.ErrInvalidRequest } - config.DefaultLeaseTTL = tmpDef + config.DefaultLeaseTTL = time.Duration(tmpDef) * time.Second } switch apiConfig.MaxLeaseTTL { case "": case "system": default: - tmpMax, err := time.ParseDuration(apiConfig.MaxLeaseTTL) + tmpMax, err := duration.ParseDurationSecond(apiConfig.MaxLeaseTTL) if err != nil { return logical.ErrorResponse(fmt.Sprintf( "unable to parse max TTL of %s: %s", apiConfig.MaxLeaseTTL, err)), logical.ErrInvalidRequest } - config.MaxLeaseTTL = tmpMax + config.MaxLeaseTTL = time.Duration(tmpMax) * time.Second } if config.MaxLeaseTTL != 0 && config.DefaultLeaseTTL > config.MaxLeaseTTL { @@ -927,11 +928,12 @@ func (b *SystemBackend) handleTuneWriteCommon( tmpDef := time.Duration(0) newDefault = &tmpDef default: - tmpDef, err := time.ParseDuration(defTTL) + tmpDef, err := duration.ParseDurationSecond(defTTL) if err != nil { return handleError(err) } - newDefault = &tmpDef + tmpDurDef := time.Duration(tmpDef) * time.Second + newDefault = &tmpDurDef } maxTTL := data.Get("max_lease_ttl").(string) @@ -941,11 +943,12 @@ func (b *SystemBackend) handleTuneWriteCommon( tmpMax := time.Duration(0) newMax = &tmpMax default: - tmpMax, err := time.ParseDuration(maxTTL) + tmpMax, err := duration.ParseDurationSecond(maxTTL) if err != nil { return handleError(err) } - newMax = &tmpMax + tmpDurMax := time.Duration(tmpMax) * time.Second + newMax = &tmpDurMax } if newDefault != nil || newMax != nil { From 58efdcba4728568cf783cb94db2388e0314aa2d0 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Mon, 11 Jul 2016 18:19:35 +0000 Subject: [PATCH 19/30] Return a duration instead and port a few other places to use it --- helper/duration/duration.go | 18 +++++++++--------- helper/duration/duration_test.go | 9 ++++++--- http/handler.go | 22 ++++++---------------- logical/framework/field_data.go | 4 ++-- vault/logical_passthrough.go | 19 ++++--------------- vault/logical_system.go | 10 ++++------ 6 files changed, 31 insertions(+), 51 deletions(-) diff --git a/helper/duration/duration.go b/helper/duration/duration.go index e3c5b683c8..f6bede0dd8 100644 --- a/helper/duration/duration.go +++ b/helper/duration/duration.go @@ -6,23 +6,23 @@ import ( "time" ) -func ParseDurationSecond(inp string) (int, error) { - var result int +func ParseDurationSecond(inp string) (time.Duration, error) { + var err error + var dur time.Duration // Look for a suffix otherwise its a plain second value if strings.HasSuffix(inp, "s") || strings.HasSuffix(inp, "m") || strings.HasSuffix(inp, "h") { - dur, err := time.ParseDuration(inp) + dur, err = time.ParseDuration(inp) if err != nil { - return result, err + return dur, err } - result = int(dur.Seconds()) } else { // Plain integer - val, err := strconv.ParseInt(inp, 10, 64) + secs, err := strconv.ParseInt(inp, 10, 64) if err != nil { - return result, err + return dur, err } - result = int(val) + dur = time.Duration(secs) * time.Second } - return result, nil + return dur, nil } diff --git a/helper/duration/duration_test.go b/helper/duration/duration_test.go index 9d3124ff45..02f0706eb0 100644 --- a/helper/duration/duration_test.go +++ b/helper/duration/duration_test.go @@ -1,20 +1,23 @@ package duration -import "testing" +import ( + "testing" + "time" +) func Test_ParseDurationSecond(t *testing.T) { outp, err := ParseDurationSecond("9876s") if err != nil { t.Fatal(err) } - if outp != 9876 { + if outp != time.Duration(9876)*time.Second { t.Fatal("not equivalent") } outp, err = ParseDurationSecond("9876") if err != nil { t.Fatal(err) } - if outp != 9876 { + if outp != time.Duration(9876)*time.Second { t.Fatal("not equivalent") } } diff --git a/http/handler.go b/http/handler.go index aef85e9756..3de72789a3 100644 --- a/http/handler.go +++ b/http/handler.go @@ -6,11 +6,10 @@ import ( "io" "net/http" "net/url" - "strconv" "strings" - "time" "github.com/hashicorp/errwrap" + "github.com/hashicorp/vault/helper/duration" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/vault" ) @@ -167,23 +166,14 @@ func requestWrapTTL(r *http.Request, req *logical.Request) (*logical.Request, er } // If it has an allowed suffix parse as a duration string - if strings.HasSuffix(wrapTTL, "s") || strings.HasSuffix(wrapTTL, "m") || strings.HasSuffix(wrapTTL, "h") { - dur, err := time.ParseDuration(wrapTTL) - if err != nil { - return req, err - } - req.WrapTTL = dur - } else { - // Parse as a straight number of seconds - seconds, err := strconv.ParseInt(wrapTTL, 10, 64) - if err != nil { - return req, err - } - req.WrapTTL = time.Duration(seconds) * time.Second + dur, err := duration.ParseDurationSecond(wrapTTL) + if err != nil { + return req, err } - if int64(req.WrapTTL) < 0 { + if int64(dur) < 0 { return req, fmt.Errorf("requested wrap ttl cannot be negative") } + req.WrapTTL = dur return req, nil } diff --git a/logical/framework/field_data.go b/logical/framework/field_data.go index 00e0f3122a..3bbd16fbc1 100644 --- a/logical/framework/field_data.go +++ b/logical/framework/field_data.go @@ -150,7 +150,6 @@ func (d *FieldData) getPrimitive( case TypeDurationSecond: var result int - var err error switch inp := raw.(type) { case nil: return nil, false, nil @@ -161,10 +160,11 @@ func (d *FieldData) getPrimitive( case float64: result = int(inp) case string: - result, err = duration.ParseDurationSecond(inp) + dur, err := duration.ParseDurationSecond(inp) if err != nil { return nil, true, err } + result = int(dur.Seconds()) default: return nil, false, fmt.Errorf("invalid input '%v'", raw) diff --git a/vault/logical_passthrough.go b/vault/logical_passthrough.go index b1fe8107dd..1d176e697b 100644 --- a/vault/logical_passthrough.go +++ b/vault/logical_passthrough.go @@ -3,10 +3,9 @@ package vault import ( "encoding/json" "fmt" - "strconv" "strings" - "time" + "github.com/hashicorp/vault/helper/duration" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical/framework" ) @@ -132,19 +131,9 @@ func (b *PassthroughBackend) handleRead( } ttlDuration := b.System().DefaultLeaseTTL() if len(ttl) != 0 { - - // Parse as a duration string if it has an appropriate suffix - if strings.HasSuffix(ttl, "s") || strings.HasSuffix(ttl, "m") || strings.HasSuffix(ttl, "h") { - dur, err := time.ParseDuration(ttl) - if err == nil { - ttlDuration = dur - } - } else { - // Parse as a straight number of seconds - seconds, err := strconv.ParseInt(ttl, 10, 64) - if err == nil { - ttlDuration = time.Duration(seconds) * time.Second - } + dur, err := duration.ParseDurationSecond(ttl) + if err == nil { + ttlDuration = dur } if b.generateLeases { diff --git a/vault/logical_system.go b/vault/logical_system.go index c3caaad8df..3b9a392ef9 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -709,7 +709,7 @@ func (b *SystemBackend) handleMount( "unable to parse default TTL of %s: %s", apiConfig.DefaultLeaseTTL, err)), logical.ErrInvalidRequest } - config.DefaultLeaseTTL = time.Duration(tmpDef) * time.Second + config.DefaultLeaseTTL = tmpDef } switch apiConfig.MaxLeaseTTL { @@ -722,7 +722,7 @@ func (b *SystemBackend) handleMount( "unable to parse max TTL of %s: %s", apiConfig.MaxLeaseTTL, err)), logical.ErrInvalidRequest } - config.MaxLeaseTTL = time.Duration(tmpMax) * time.Second + config.MaxLeaseTTL = tmpMax } if config.MaxLeaseTTL != 0 && config.DefaultLeaseTTL > config.MaxLeaseTTL { @@ -932,8 +932,7 @@ func (b *SystemBackend) handleTuneWriteCommon( if err != nil { return handleError(err) } - tmpDurDef := time.Duration(tmpDef) * time.Second - newDefault = &tmpDurDef + newDefault = &tmpDef } maxTTL := data.Get("max_lease_ttl").(string) @@ -947,8 +946,7 @@ func (b *SystemBackend) handleTuneWriteCommon( if err != nil { return handleError(err) } - tmpDurMax := time.Duration(tmpMax) * time.Second - newMax = &tmpDurMax + newMax = &tmpMax } if newDefault != nil || newMax != nil { From 7129fd57856cca710e728436cba1e4d281ca79ce Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Mon, 11 Jul 2016 21:37:46 +0000 Subject: [PATCH 20/30] Switch to pester from go-retryablehttp to avoid swallowing 500 error messages --- CHANGELOG.md | 8 +- api/client.go | 87 ++------- api/client_test.go | 17 +- api/request.go | 23 +-- builtin/logical/transit/policy.go | 2 +- vendor/github.com/kardianos/govendor/LICENSE | 27 +++ .../github.com/kardianos/govendor/README.md | 181 ++++++++++++++++++ vendor/github.com/kardianos/govendor/main.go | 52 +++++ vendor/vendor.json | 6 + .../source/docs/commands/environment.html.md | 12 +- 10 files changed, 298 insertions(+), 117 deletions(-) create mode 100644 vendor/github.com/kardianos/govendor/LICENSE create mode 100644 vendor/github.com/kardianos/govendor/README.md create mode 100644 vendor/github.com/kardianos/govendor/main.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 4310ffa399..973fe6d092 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,6 @@ DEPRECATIONS/BREAKING CHANGES: * Issued certificates from the `pki` backend against new roles created or modified after upgrading will contain a set of default key usages. - * In the Go API, the function signature for `Request.ToHTTP()` has changed. FEATURES: @@ -20,10 +19,9 @@ FEATURES: compatibility with OpenVPN and some other software. This set can be changed when writing a role definition. Existing roles are unaffected. [GH-1552] * **Request Retrying in the CLI and Go API**: Requests that fail with a `5xx` - error code will now retry after a backoff. The minimum and maximum backoff - times, as well as the maximum total number of retries (including disabling - this functionality) can be set with environment variables. See the - [environment variable + error code will now retry after a backoff. The maximum total number of + retries (including disabling this functionality) can be set with an + environment variable. See the [environment variable documentation](https://www.vaultproject.io/docs/commands/environment.html) for more details. [GH-1594] diff --git a/api/client.go b/api/client.go index f7036ccc89..4158f2633e 100644 --- a/api/client.go +++ b/api/client.go @@ -13,8 +13,8 @@ import ( "time" "github.com/hashicorp/go-cleanhttp" - "github.com/hashicorp/go-retryablehttp" "github.com/hashicorp/go-rootcerts" + "github.com/sethgrid/pester" ) const EnvVaultAddress = "VAULT_ADDR" @@ -25,9 +25,7 @@ const EnvVaultClientKey = "VAULT_CLIENT_KEY" const EnvVaultInsecure = "VAULT_SKIP_VERIFY" const EnvVaultTLSServerName = "VAULT_TLS_SERVER_NAME" const EnvVaultWrapTTL = "VAULT_WRAP_TTL" -const EnvVaultRetryWaitMin = "VAULT_RETRY_WAIT_MIN" -const EnvVaultRetryWaitMax = "VAULT_RETRY_WAIT_MAX" -const EnvVaultRetryMax = "VAULT_RETRY_MAX" +const EnvVaultMaxRetries = "VAULT_MAX_RETRIES" var ( errRedirect = errors.New("redirect") @@ -54,17 +52,9 @@ type Config struct { redirectSetup sync.Once - // RetryWaitMin controls the minimum amount of time to wait between retries - // when a 5xx error occurs - RetryWaitMin time.Duration - - // RetryWaitMax controls the maximum amount of time to wait between retries - // when a 5xx error occurs - RetryWaitMax time.Duration - - // RetryMax controls the maximum number of times to retry when a 5xx error - // occurs. Set to 0 to disable retrying. - RetryMax int + // MaxRetries controls the maximum number of times to retry when a 5xx error + // occurs. Set to 1 or less to disable retrying. + MaxRetries int } // DefaultConfig returns a default configuration for the client. It is @@ -89,9 +79,7 @@ func DefaultConfig() *Config { config.Address = v } - config.RetryWaitMin = 1 * time.Second - config.RetryWaitMax = 30 * time.Second - config.RetryMax = 15 + config.MaxRetries = pester.DefaultClient.MaxRetries return config } @@ -109,9 +97,7 @@ func (c *Config) ReadEnvironment() error { var foundInsecure bool var envTLSServerName string - var envRetryWaitMin *time.Duration - var envRetryWaitMax *time.Duration - var envRetryMax *uint64 + var envMaxRetries *uint64 var clientCert tls.Certificate var foundClientCert bool @@ -120,44 +106,13 @@ func (c *Config) ReadEnvironment() error { if v := os.Getenv(EnvVaultAddress); v != "" { envAddress = v } - - // Handle retry parameters - { - if v := os.Getenv(EnvVaultRetryWaitMin); v != "" { - waitMin, err := time.ParseDuration(v) - if err != nil { - return err - } - envRetryWaitMin = &waitMin - } - if v := os.Getenv(EnvVaultRetryWaitMax); v != "" { - waitMax, err := time.ParseDuration(v) - if err != nil { - return err - } - envRetryWaitMax = &waitMax - } - if v := os.Getenv(EnvVaultRetryMax); v != "" { - retryMax, err := strconv.ParseUint(v, 10, 32) - if err != nil { - return err - } - envRetryMax = &retryMax - } - - min := c.RetryWaitMin - if envRetryWaitMin != nil { - min = *envRetryWaitMin - } - max := c.RetryWaitMax - if envRetryWaitMax != nil { - max = *envRetryWaitMax - } - if min > max { - return fmt.Errorf("Maximum retry delay is less than minimum retry delay") + if v := os.Getenv(EnvVaultMaxRetries); v != "" { + maxRetries, err := strconv.ParseUint(v, 10, 32) + if err != nil { + return err } + envMaxRetries = &maxRetries } - if v := os.Getenv(EnvVaultCACert); v != "" { envCACert = v } @@ -208,14 +163,8 @@ func (c *Config) ReadEnvironment() error { c.Address = envAddress } - if envRetryWaitMin != nil { - c.RetryWaitMin = *envRetryWaitMin - } - if envRetryWaitMax != nil { - c.RetryWaitMax = *envRetryWaitMax - } - if envRetryMax != nil { - c.RetryMax = int(*envRetryMax) + if envMaxRetries != nil { + c.MaxRetries = int(*envMaxRetries) } if foundInsecure { @@ -345,11 +294,9 @@ START: return nil, err } - client := retryablehttp.NewClient() - client.HTTPClient = c.config.HttpClient - client.RetryWaitMax = c.config.RetryWaitMax - client.RetryWaitMin = c.config.RetryWaitMin - client.RetryMax = c.config.RetryMax + client := pester.NewExtendedClient(c.config.HttpClient) + client.Backoff = pester.LinearJitterBackoff + client.MaxRetries = c.config.MaxRetries var result *Response resp, err := client.Do(req) diff --git a/api/client_test.go b/api/client_test.go index 2d6df0ea5e..b0deee96ac 100644 --- a/api/client_test.go +++ b/api/client_test.go @@ -107,25 +107,19 @@ func TestClientEnvSettings(t *testing.T) { oldClientCert := os.Getenv(EnvVaultClientCert) oldClientKey := os.Getenv(EnvVaultClientKey) oldSkipVerify := os.Getenv(EnvVaultInsecure) - oldRetryWaitMin := os.Getenv(EnvVaultRetryWaitMin) - oldRetryWaitMax := os.Getenv(EnvVaultRetryWaitMax) - oldRetryMax := os.Getenv(EnvVaultRetryMax) + oldMaxRetries := os.Getenv(EnvVaultMaxRetries) os.Setenv(EnvVaultCACert, cwd+"/test-fixtures/keys/cert.pem") os.Setenv(EnvVaultCAPath, cwd+"/test-fixtures/keys") os.Setenv(EnvVaultClientCert, cwd+"/test-fixtures/keys/cert.pem") os.Setenv(EnvVaultClientKey, cwd+"/test-fixtures/keys/key.pem") os.Setenv(EnvVaultInsecure, "true") - os.Setenv(EnvVaultRetryWaitMin, "20s") - os.Setenv(EnvVaultRetryWaitMax, "25s") - os.Setenv(EnvVaultRetryMax, "20") + os.Setenv(EnvVaultMaxRetries, "5") defer os.Setenv(EnvVaultCACert, oldCACert) defer os.Setenv(EnvVaultCAPath, oldCAPath) defer os.Setenv(EnvVaultClientCert, oldClientCert) defer os.Setenv(EnvVaultClientKey, oldClientKey) defer os.Setenv(EnvVaultInsecure, oldSkipVerify) - defer os.Setenv(EnvVaultRetryWaitMin, oldRetryWaitMin) - defer os.Setenv(EnvVaultRetryWaitMax, oldRetryWaitMax) - defer os.Setenv(EnvVaultRetryMax, oldRetryMax) + defer os.Setenv(EnvVaultMaxRetries, oldMaxRetries) config := DefaultConfig() if err := config.ReadEnvironment(); err != nil { @@ -142,9 +136,4 @@ func TestClientEnvSettings(t *testing.T) { if tlsConfig.InsecureSkipVerify != true { t.Fatalf("bad: %v", tlsConfig.InsecureSkipVerify) } - - os.Setenv(EnvVaultRetryWaitMax, "15s") - if err := config.ReadEnvironment(); err == nil { - t.Fatal("expected error due to max retry time being less than min") - } } diff --git a/api/request.go b/api/request.go index 16062e7cf0..8f22dd5725 100644 --- a/api/request.go +++ b/api/request.go @@ -3,11 +3,9 @@ package api import ( "bytes" "encoding/json" - "fmt" "io" + "net/http" "net/url" - - "github.com/hashicorp/go-retryablehttp" ) // Request is a raw request configuration structure used to initiate @@ -45,23 +43,14 @@ func (r *Request) ResetJSONBody() error { return r.SetJSONBody(r.Obj) } -// ToHTTP turns this request into a *retryablehttp.Request -func (r *Request) ToHTTP() (*retryablehttp.Request, error) { +// ToHTTP turns this request into a valid *http.Request for use with the +// net/http package. +func (r *Request) ToHTTP() (*http.Request, error) { // Encode the query parameters r.URL.RawQuery = r.Params.Encode() - // Create the HTTP request; retryable needs a ReadSeeker - body := bytes.NewBuffer(nil) - if r.Body != nil { - n, err := body.ReadFrom(r.Body) - if err != nil { - return nil, err - } - if n != r.BodySize { - return nil, fmt.Errorf("Could not read full body size from Request") - } - } - req, err := retryablehttp.NewRequest(r.Method, r.URL.RequestURI(), bytes.NewReader(body.Bytes())) + // Create the HTTP request + req, err := http.NewRequest(r.Method, r.URL.RequestURI(), r.Body) if err != nil { return nil, err } diff --git a/builtin/logical/transit/policy.go b/builtin/logical/transit/policy.go index b9ede365c8..02e1da8941 100644 --- a/builtin/logical/transit/policy.go +++ b/builtin/logical/transit/policy.go @@ -345,7 +345,7 @@ func (p *Policy) Encrypt(context []byte, value string) (string, error) { // Derive the key that should be used key, err := p.DeriveKey(context, p.LatestVersion) if err != nil { - return "", certutil.InternalError{Err: err.Error()} + return "", err } // Guard against a potentially invalid cipher-mode diff --git a/vendor/github.com/kardianos/govendor/LICENSE b/vendor/github.com/kardianos/govendor/LICENSE new file mode 100644 index 0000000000..d29b37261f --- /dev/null +++ b/vendor/github.com/kardianos/govendor/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2015 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/kardianos/govendor/README.md b/vendor/github.com/kardianos/govendor/README.md new file mode 100644 index 0000000000..abad7693d3 --- /dev/null +++ b/vendor/github.com/kardianos/govendor/README.md @@ -0,0 +1,181 @@ +# The Vendor Tool for Go +`go get -u github.com/kardianos/govendor` + +New users please read the [FAQ](doc/faq.md) + +Package developers should read the [developer guide](doc/dev-guide.md). + +For a high level overview read the [whitepaper](doc/whitepaper.md) + +Uses the go1.5+ vendor folder. Multiple workflows supported, single tool. + +[![Build Status](https://travis-ci.org/kardianos/govendor.svg?branch=master)](https://travis-ci.org/kardianos/govendor) +[![GoDoc](https://godoc.org/github.com/kardianos/govendor?status.svg)](https://godoc.org/github.com/kardianos/govendor) + + * Copy existing dependencies from $GOPATH with `govendor add/update`. + * If you ignore `vendor/*/`, restore dependencies with `govendor sync`. + * Pull in new dependencies or update existing dependencies directly from + remotes with `govendor fetch`. + * Migrate from legacy systems with `govendor migrate`. + * Supports Linux, OS X, Windows, probably all others. + * Supports git, hg, svn, bzr (must be installed an on the PATH). + +## Notes + + * The project must be within a $GOPATH. + * If using go1.5, ensure you `set GO15VENDOREXPERIMENT=1`. + +### Quick Start, also see the [FAQ](doc/faq.md) +``` +# Setup your project. +cd "my project in GOPATH" +govendor init + +# Add existing GOPATH files to vendor. +govendor add +external + +# View your work. +govendor list + +# Look at what is using a package +govendor list -v fmt + +# Specify a specific version or revision to fetch +govendor fetch golang.org/x/net/context@a4bbce9fcae005b22ae5443f6af064d80a6f5a55 +govendor fetch golang.org/x/net/context@v1 # Get latest v1.*.* tag or branch. +govendor fetch golang.org/x/net/context@=v1 # Get the tag or branch named "v1". + +# Update a package to latest, given any prior version constraint +govendor fetch golang.org/x/net/context + +# Format your repository only +govendor fmt +local + +# Build everything in your repository only +govendor install +local + +# Test your repository only +govendor test +local + +``` + +## Sub-commands +``` + init Create the "vendor" folder and the "vendor.json" file. + list List and filter existing dependencies and packages. + add Add packages from $GOPATH. + update Update packages from $GOPATH. + remove Remove packages from the vendor folder. + status Lists any packages missing, out-of-date, or modified locally. + fetch Add new or update vendor folder packages from remote repository. + sync Pull packages into vendor folder from remote repository with revisions + from vendor.json file. + migrate Move packages from a legacy tool to the vendor folder with metadata. + get Like "go get" but copies dependencies into a "vendor" folder. + license List discovered licenses for the given status or import paths. + shell Run a "shell" to make multiple sub-commands more efficient for large + projects. + + go tool commands that are wrapped: + `+` package selection may be used with them + fmt, build, install, clean, test, vet, generate +``` + +## Status + +Packages can be specified by their "status". +``` + +local (l) packages in your project + +external (e) referenced packages in GOPATH but not in current project + +vendor (v) packages in the vendor folder + +std (s) packages in the standard library + + +excluded (x) external packages explicitely excluded from vendoring + +unused (u) packages in the vendor folder, but unused + +missing (m) referenced packages but not found + + +program (p) package is a main package + + +outside +external +missing + +all +all packages +``` + +Status can be referenced by their initial letters. + + * `+std` same as `+s` + * `+external` same as `+ext` same as `+e` + * `+excluded` same as `+exc` same as `+x` + +Status can be logically composed: + + * `+local,program` (local AND program) local packages that are also programs + * `+local +vendor` (local OR vendor) local packages or vendor packages + * `+vendor,program +std` ((vendor AND program) OR std) vendor packages that are also programs + or std library packages + * `+vendor,^program` (vendor AND NOT program) vendor package that are not "main" packages. + +## Package specifier + +The full package-spec is: +`[::][{/...|/^}][@[]]` + +Some examples: + + * `github.com/kardianos/govendor` specifies a single package and single folder. + * `github.com/kardianos/govendor/...` specifies `govendor` and all referenced + packages under that path. + * `github.com/kardianos/govendor/^` specifies the `govendor` folder and all + sub-folders. Useful for resources or if you don't want a partial repository. + * `github.com/kardianos/govendor/^::github.com/myself/govendor` same as above + but fetch from user "myself". + * `github.com/kardianos/govendor/...@abc12032` all referenced packages at + revision `abc12032`. + * `github.com/kardianos/govendor/...@v1` same as above, but get the most recent + "v1" tag, such as "v1.4.3". + * `github.com/kardianos/govendor/...@=v1` get the exact version "v1". + +## Packages and Status + +You may specify multiple package-specs and multiple status in a single command. +Commands that accept status and package-spec: + + * list + * add + * update + * remove + * fetch + +You may pass arguments to govendor through stdin if the last argument is a "-". +For example `echo +vendor | govendor list -` will list all vendor packages. + +## Ignoring build tags and excluding packages +Ignoring build tags is opt-out and is designed to be the opposite of the build +file directives which are opt-in when specified. Typically a developer will +want to support cross platform builds, but selectively opt out of tags, tests, +and architectures as desired. + +To ignore additional tags edit the "vendor.json" file and add tag to the vendor +"ignore" file field. The field uses spaces to separate tags to ignore. +For example the following will ignore both test and appengine files. +``` +{ + "ignore": "test appengine", +} +``` + +Similarly, some specific packages can be excluded from the vendoring process. +These packages will be listed as `excluded` (`x`), and will not be copied to the +"vendor" folder when running `govendor add|fetch|update`. + +Any sub-package `foo/bar` of an excluded package `foo` is also excluded (but +package `bar/foo` is not). The import dependencies of excluded packages are not +listed, and thus not vendored. + +To exclude packages, also use the "ignore" field of the "vendor.json" file. +Packages are identified by their name, they should contain a "/" character +(possibly at the end): +``` +{ + "ignore": "test appengine foo/", +} +``` diff --git a/vendor/github.com/kardianos/govendor/main.go b/vendor/github.com/kardianos/govendor/main.go new file mode 100644 index 0000000000..c5884871a8 --- /dev/null +++ b/vendor/github.com/kardianos/govendor/main.go @@ -0,0 +1,52 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// vendor tool to copy external source code from GOPATH or remote location to the +// local vendor folder. See README.md for usage. +package main + +import ( + "bytes" + "flag" + "fmt" + "io" + "os" + "strings" + + "github.com/kardianos/govendor/cliprompt" + "github.com/kardianos/govendor/help" + "github.com/kardianos/govendor/run" +) + +func main() { + prompt := &cliprompt.Prompt{} + + allArgs := os.Args + + if allArgs[len(allArgs)-1] == "-" { + stdin := &bytes.Buffer{} + if _, err := io.Copy(stdin, os.Stdin); err == nil { + stdinArgs := strings.Fields(stdin.String()) + allArgs = append(allArgs[:len(allArgs)-1], stdinArgs...) + } + } + + msg, err := run.Run(os.Stdout, allArgs, prompt) + if err == flag.ErrHelp { + err = nil + } + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + } + msgText := msg.String() + if len(msgText) > 0 { + fmt.Fprint(os.Stderr, msgText) + } + if err != nil { + os.Exit(2) + } + if msg != help.MsgNone { + os.Exit(1) + } +} diff --git a/vendor/vendor.json b/vendor/vendor.json index c208bca7de..ffa1993620 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -574,6 +574,12 @@ "revision": "0b12d6b521d83fc7f755e7cfc1b1fbdd35a01a74", "revisionTime": "2016-02-02T18:50:14Z" }, + { + "checksumSHA1": "bQfc4zGh8WUri465COdQNhJJTw4=", + "path": "github.com/kardianos/govendor", + "revision": "7a0d30eab9e67b3ff60f13cdc483f003c01e04c1", + "revisionTime": "2016-07-09T17:43:04Z" + }, { "checksumSHA1": "QK3MNUdQwUBuznCHZcPijU/3DyI=", "path": "github.com/lib/pq", diff --git a/website/source/docs/commands/environment.html.md b/website/source/docs/commands/environment.html.md index d54e7cc679..c3adee7c51 100644 --- a/website/source/docs/commands/environment.html.md +++ b/website/source/docs/commands/environment.html.md @@ -47,16 +47,8 @@ The following table describes them: Path to an unencrypted PEM-encoded private key matching the client certificate. - VAULT_RETRY_MAX - The maximum number of retries when a `5xx` error code is encountered. Default is `15`; set to `0` to disable retrying. - - - VAULT_RETRY_WAIT_MIN - The minimum amount of time to wait between retries when a `5xx` error code is encountered. Default is `1s`. - - - VAULT_RETRY_WAIT_MAX - The maximum amount of time to wait between retries when a `5xx` error code is encountered. Default is `30s`. + VAULT_MAX_RETRIES + The maximum number of retries when a `5xx` error code is encountered. Default is `3`; set to `1` or less to disable retrying. VAULT_SKIP_VERIFY From 478f420912417fc34f3cfc5925d53b29f5395039 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Mon, 11 Jul 2016 21:57:14 +0000 Subject: [PATCH 21/30] Migrate number of retries down by one to have it be max retries, not tries --- api/client.go | 4 ++-- website/source/docs/commands/environment.html.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/client.go b/api/client.go index 4158f2633e..d9b5fa0de5 100644 --- a/api/client.go +++ b/api/client.go @@ -53,7 +53,7 @@ type Config struct { redirectSetup sync.Once // MaxRetries controls the maximum number of times to retry when a 5xx error - // occurs. Set to 1 or less to disable retrying. + // occurs. Set to 0 or less to disable retrying. MaxRetries int } @@ -164,7 +164,7 @@ func (c *Config) ReadEnvironment() error { } if envMaxRetries != nil { - c.MaxRetries = int(*envMaxRetries) + c.MaxRetries = int(*envMaxRetries) + 1 } if foundInsecure { diff --git a/website/source/docs/commands/environment.html.md b/website/source/docs/commands/environment.html.md index c3adee7c51..198d1906b6 100644 --- a/website/source/docs/commands/environment.html.md +++ b/website/source/docs/commands/environment.html.md @@ -48,7 +48,7 @@ The following table describes them: VAULT_MAX_RETRIES - The maximum number of retries when a `5xx` error code is encountered. Default is `3`; set to `1` or less to disable retrying. + The maximum number of retries when a `5xx` error code is encountered. Default is `2`, for three total tries; set to `0` or less to disable retrying. VAULT_SKIP_VERIFY From 97360e55bdc33f2cb9a1049edf1e42a794e30520 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Mon, 11 Jul 2016 23:13:26 +0000 Subject: [PATCH 22/30] Whoops, fix vendoring --- vendor/github.com/kardianos/govendor/LICENSE | 27 -- .../github.com/kardianos/govendor/README.md | 181 -------- vendor/github.com/kardianos/govendor/main.go | 52 --- vendor/github.com/sethgrid/pester/LICENSE.md | 21 + vendor/github.com/sethgrid/pester/README.md | 126 ++++++ vendor/github.com/sethgrid/pester/main.go | 423 ++++++++++++++++++ vendor/vendor.json | 12 +- 7 files changed, 576 insertions(+), 266 deletions(-) delete mode 100644 vendor/github.com/kardianos/govendor/LICENSE delete mode 100644 vendor/github.com/kardianos/govendor/README.md delete mode 100644 vendor/github.com/kardianos/govendor/main.go create mode 100644 vendor/github.com/sethgrid/pester/LICENSE.md create mode 100644 vendor/github.com/sethgrid/pester/README.md create mode 100644 vendor/github.com/sethgrid/pester/main.go diff --git a/vendor/github.com/kardianos/govendor/LICENSE b/vendor/github.com/kardianos/govendor/LICENSE deleted file mode 100644 index d29b37261f..0000000000 --- a/vendor/github.com/kardianos/govendor/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2015 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/kardianos/govendor/README.md b/vendor/github.com/kardianos/govendor/README.md deleted file mode 100644 index abad7693d3..0000000000 --- a/vendor/github.com/kardianos/govendor/README.md +++ /dev/null @@ -1,181 +0,0 @@ -# The Vendor Tool for Go -`go get -u github.com/kardianos/govendor` - -New users please read the [FAQ](doc/faq.md) - -Package developers should read the [developer guide](doc/dev-guide.md). - -For a high level overview read the [whitepaper](doc/whitepaper.md) - -Uses the go1.5+ vendor folder. Multiple workflows supported, single tool. - -[![Build Status](https://travis-ci.org/kardianos/govendor.svg?branch=master)](https://travis-ci.org/kardianos/govendor) -[![GoDoc](https://godoc.org/github.com/kardianos/govendor?status.svg)](https://godoc.org/github.com/kardianos/govendor) - - * Copy existing dependencies from $GOPATH with `govendor add/update`. - * If you ignore `vendor/*/`, restore dependencies with `govendor sync`. - * Pull in new dependencies or update existing dependencies directly from - remotes with `govendor fetch`. - * Migrate from legacy systems with `govendor migrate`. - * Supports Linux, OS X, Windows, probably all others. - * Supports git, hg, svn, bzr (must be installed an on the PATH). - -## Notes - - * The project must be within a $GOPATH. - * If using go1.5, ensure you `set GO15VENDOREXPERIMENT=1`. - -### Quick Start, also see the [FAQ](doc/faq.md) -``` -# Setup your project. -cd "my project in GOPATH" -govendor init - -# Add existing GOPATH files to vendor. -govendor add +external - -# View your work. -govendor list - -# Look at what is using a package -govendor list -v fmt - -# Specify a specific version or revision to fetch -govendor fetch golang.org/x/net/context@a4bbce9fcae005b22ae5443f6af064d80a6f5a55 -govendor fetch golang.org/x/net/context@v1 # Get latest v1.*.* tag or branch. -govendor fetch golang.org/x/net/context@=v1 # Get the tag or branch named "v1". - -# Update a package to latest, given any prior version constraint -govendor fetch golang.org/x/net/context - -# Format your repository only -govendor fmt +local - -# Build everything in your repository only -govendor install +local - -# Test your repository only -govendor test +local - -``` - -## Sub-commands -``` - init Create the "vendor" folder and the "vendor.json" file. - list List and filter existing dependencies and packages. - add Add packages from $GOPATH. - update Update packages from $GOPATH. - remove Remove packages from the vendor folder. - status Lists any packages missing, out-of-date, or modified locally. - fetch Add new or update vendor folder packages from remote repository. - sync Pull packages into vendor folder from remote repository with revisions - from vendor.json file. - migrate Move packages from a legacy tool to the vendor folder with metadata. - get Like "go get" but copies dependencies into a "vendor" folder. - license List discovered licenses for the given status or import paths. - shell Run a "shell" to make multiple sub-commands more efficient for large - projects. - - go tool commands that are wrapped: - `+` package selection may be used with them - fmt, build, install, clean, test, vet, generate -``` - -## Status - -Packages can be specified by their "status". -``` - +local (l) packages in your project - +external (e) referenced packages in GOPATH but not in current project - +vendor (v) packages in the vendor folder - +std (s) packages in the standard library - - +excluded (x) external packages explicitely excluded from vendoring - +unused (u) packages in the vendor folder, but unused - +missing (m) referenced packages but not found - - +program (p) package is a main package - - +outside +external +missing - +all +all packages -``` - -Status can be referenced by their initial letters. - - * `+std` same as `+s` - * `+external` same as `+ext` same as `+e` - * `+excluded` same as `+exc` same as `+x` - -Status can be logically composed: - - * `+local,program` (local AND program) local packages that are also programs - * `+local +vendor` (local OR vendor) local packages or vendor packages - * `+vendor,program +std` ((vendor AND program) OR std) vendor packages that are also programs - or std library packages - * `+vendor,^program` (vendor AND NOT program) vendor package that are not "main" packages. - -## Package specifier - -The full package-spec is: -`[::][{/...|/^}][@[]]` - -Some examples: - - * `github.com/kardianos/govendor` specifies a single package and single folder. - * `github.com/kardianos/govendor/...` specifies `govendor` and all referenced - packages under that path. - * `github.com/kardianos/govendor/^` specifies the `govendor` folder and all - sub-folders. Useful for resources or if you don't want a partial repository. - * `github.com/kardianos/govendor/^::github.com/myself/govendor` same as above - but fetch from user "myself". - * `github.com/kardianos/govendor/...@abc12032` all referenced packages at - revision `abc12032`. - * `github.com/kardianos/govendor/...@v1` same as above, but get the most recent - "v1" tag, such as "v1.4.3". - * `github.com/kardianos/govendor/...@=v1` get the exact version "v1". - -## Packages and Status - -You may specify multiple package-specs and multiple status in a single command. -Commands that accept status and package-spec: - - * list - * add - * update - * remove - * fetch - -You may pass arguments to govendor through stdin if the last argument is a "-". -For example `echo +vendor | govendor list -` will list all vendor packages. - -## Ignoring build tags and excluding packages -Ignoring build tags is opt-out and is designed to be the opposite of the build -file directives which are opt-in when specified. Typically a developer will -want to support cross platform builds, but selectively opt out of tags, tests, -and architectures as desired. - -To ignore additional tags edit the "vendor.json" file and add tag to the vendor -"ignore" file field. The field uses spaces to separate tags to ignore. -For example the following will ignore both test and appengine files. -``` -{ - "ignore": "test appengine", -} -``` - -Similarly, some specific packages can be excluded from the vendoring process. -These packages will be listed as `excluded` (`x`), and will not be copied to the -"vendor" folder when running `govendor add|fetch|update`. - -Any sub-package `foo/bar` of an excluded package `foo` is also excluded (but -package `bar/foo` is not). The import dependencies of excluded packages are not -listed, and thus not vendored. - -To exclude packages, also use the "ignore" field of the "vendor.json" file. -Packages are identified by their name, they should contain a "/" character -(possibly at the end): -``` -{ - "ignore": "test appengine foo/", -} -``` diff --git a/vendor/github.com/kardianos/govendor/main.go b/vendor/github.com/kardianos/govendor/main.go deleted file mode 100644 index c5884871a8..0000000000 --- a/vendor/github.com/kardianos/govendor/main.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// vendor tool to copy external source code from GOPATH or remote location to the -// local vendor folder. See README.md for usage. -package main - -import ( - "bytes" - "flag" - "fmt" - "io" - "os" - "strings" - - "github.com/kardianos/govendor/cliprompt" - "github.com/kardianos/govendor/help" - "github.com/kardianos/govendor/run" -) - -func main() { - prompt := &cliprompt.Prompt{} - - allArgs := os.Args - - if allArgs[len(allArgs)-1] == "-" { - stdin := &bytes.Buffer{} - if _, err := io.Copy(stdin, os.Stdin); err == nil { - stdinArgs := strings.Fields(stdin.String()) - allArgs = append(allArgs[:len(allArgs)-1], stdinArgs...) - } - } - - msg, err := run.Run(os.Stdout, allArgs, prompt) - if err == flag.ErrHelp { - err = nil - } - if err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) - } - msgText := msg.String() - if len(msgText) > 0 { - fmt.Fprint(os.Stderr, msgText) - } - if err != nil { - os.Exit(2) - } - if msg != help.MsgNone { - os.Exit(1) - } -} diff --git a/vendor/github.com/sethgrid/pester/LICENSE.md b/vendor/github.com/sethgrid/pester/LICENSE.md new file mode 100644 index 0000000000..907f3a2007 --- /dev/null +++ b/vendor/github.com/sethgrid/pester/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) [2016] [Seth Ammons] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/sethgrid/pester/README.md b/vendor/github.com/sethgrid/pester/README.md new file mode 100644 index 0000000000..e41f4d6f66 --- /dev/null +++ b/vendor/github.com/sethgrid/pester/README.md @@ -0,0 +1,126 @@ +# pester + +`pester` wraps Go's standard lib http client to provide several options to increase resiliency in your request. If you experience poor network conditions or requests could experience varied delays, you can now pester the endpoint for data. +- Send out multiple requests and get the first back (only used for GET calls) +- Retry on errors +- Backoff + +### Simple Example +Use `pester` where you would use the http client calls. By default, pester will use a concurrency of 1, and retry the endpoint 3 times with the `DefaultBackoff` strategy of waiting 1 second between retries. +```go +/* swap in replacement, just switch + http.{Get|Post|PostForm|Head|Do} to + pester.{Get|Post|PostForm|Head|Do} +*/ +resp, err := pester.Get("http://sethammons.com") +``` + +### Backoff Strategy +Provide your own backoff strategy, or use one of the provided built in strategies: +- `DefaultBackoff`: 1 second +- `LinearBackoff`: n seconds where n is the retry number +- `LinearJitterBackoff`: n seconds where n is the retry number, +/- 0-33% +- `ExponentialBackoff`: n seconds where n is 2^(retry number) +- `ExponentialJitterBackoff`: n seconds where n is 2^(retry number), +/- 0-33% + +```go +client := pester.New() +client.Backoff = func(retry int) time.Duration { + // set up something dynamic or use a look up table + return time.Duration(retry) * time.Minute +} +``` + +### Complete example +For a complete and working example, see the sample directory. +`pester` allows you to use a constructor to control: +- backoff strategy +- reties +- concurrency +- keeping a log for debugging +```go +package main + +import ( + "log" + "net/http" + "strings" + + "github.com/sethgrid/pester" +) + +func main() { + log.Println("Starting...") + + { // drop in replacement for http.Get and other client methods + resp, err := pester.Get("http://example.com") + if err != nil { + log.Println("error GETing example.com", err) + } + defer resp.Body.Close() + log.Printf("example.com %s", resp.Status) + } + + { // control the resiliency + client := pester.New() + client.Concurrency = 3 + client.MaxRetries = 5 + client.Backoff = pester.ExponentialBackoff + client.KeepLog = true + + resp, err := client.Get("http://example.com") + if err != nil { + log.Println("error GETing example.com", client.LogString()) + } + defer resp.Body.Close() + log.Printf("example.com %s", resp.Status) + } + + { // use the pester version of http.Client.Do + req, err := http.NewRequest("POST", "http://example.com", strings.NewReader("data")) + if err != nil { + log.Fatal("Unable to create a new http request", err) + } + resp, err := pester.Do(req) + if err != nil { + log.Println("error POSTing example.com", err) + } + defer resp.Body.Close() + log.Printf("example.com %s", resp.Status) + } +} + +``` + +### Example Log +`pester` also allows you to control the resiliency and can optionally log the errors. +```go +c := pester.New() +c.KeepLog = true + +nonExistantURL := "http://localhost:9000/foo" +_, _ = c.Get(nonExistantURL) + +fmt.Println(c.LogString()) +/* +Output: + +1432402837 Get [GET] http://localhost:9000/foo request-0 retry-0 error: Get http://localhost:9000/foo: dial tcp 127.0.0.1:9000: connection refused +1432402838 Get [GET] http://localhost:9000/foo request-0 retry-1 error: Get http://localhost:9000/foo: dial tcp 127.0.0.1:9000: connection refused +1432402839 Get [GET] http://localhost:9000/foo request-0 retry-2 error: Get http://localhost:9000/foo: dial tcp 127.0.0.1:9000: connection refused +*/ +``` + +### Tests + +You can run tests in the root directory with `$ go test`. There is a benchmark-like test available with `$ cd benchmarks; go test`. +You can see `pester` in action with `$ cd sample; go run main.go`. + +For watching open file descriptors, you can run `watch "lsof -i -P | grep main"` if you started the app with `go run main.go`. +I did this for watching for FD leaks. My method was to alter `sample/main.go` to only run one case (`pester.Get with set backoff stategy, concurrency and retries increased`) +and adding a sleep after the result came back. This let me verify if FDs were getting left open when they should have closed. If you know a better way, let me know! +I was able to see that FDs are now closing when they should :) + +![Are we there yet?](http://butchbellah.com/wp-content/uploads/2012/06/Are-We-There-Yet.jpg) + +Are we there yet? Are we there yet? Are we there yet? Are we there yet? ... diff --git a/vendor/github.com/sethgrid/pester/main.go b/vendor/github.com/sethgrid/pester/main.go new file mode 100644 index 0000000000..8eb91fe52b --- /dev/null +++ b/vendor/github.com/sethgrid/pester/main.go @@ -0,0 +1,423 @@ +package pester + +// pester provides additional resiliency over the standard http client methods by +// allowing you to control concurrency, retries, and a backoff strategy. + +import ( + "bytes" + "errors" + "fmt" + "io" + "io/ioutil" + "math" + "math/rand" + "net/http" + "net/url" + "sync" + "time" +) + +// Client wraps the http client and exposes all the functionality of the http.Client. +// Additionally, Client provides pester specific values for handling resiliency. +type Client struct { + // wrap it to provide access to http built ins + hc *http.Client + + Transport http.RoundTripper + CheckRedirect func(req *http.Request, via []*http.Request) error + Jar http.CookieJar + Timeout time.Duration + + // pester specific + Concurrency int + MaxRetries int + Backoff BackoffStrategy + KeepLog bool + + SuccessReqNum int + SuccessRetryNum int + + wg *sync.WaitGroup + + sync.Mutex + ErrLog []ErrEntry +} + +// ErrEntry is used to provide the LogString() data and is populated +// each time an error happens if KeepLog is set. +// ErrEntry.Retry is deprecated in favor of ErrEntry.Attempt +type ErrEntry struct { + Time time.Time + Method string + URL string + Verb string + Request int + Retry int + Attempt int + Err error +} + +// result simplifies the channel communication for concurrent request handling +type result struct { + resp *http.Response + err error + req int + retry int +} + +// params represents all the params needed to run http client calls and pester errors +type params struct { + method string + verb string + req *http.Request + url string + bodyType string + body io.Reader + data url.Values +} + +// New constructs a new DefaultClient with sensible default values +func New() *Client { + return &Client{ + Concurrency: DefaultClient.Concurrency, + MaxRetries: DefaultClient.MaxRetries, + Backoff: DefaultClient.Backoff, + ErrLog: DefaultClient.ErrLog, + wg: &sync.WaitGroup{}, + } +} + +// NewExtendedClient allows you to pass in an http.Client that is previously set up +// and extends it to have Pester's features of concurrency and retries. +func NewExtendedClient(hc *http.Client) *Client { + c := New() + c.hc = hc + return c +} + +// BackoffStrategy is used to determine how long a retry request should wait until attempted +type BackoffStrategy func(retry int) time.Duration + +// DefaultClient provides sensible defaults +var DefaultClient = &Client{Concurrency: 1, MaxRetries: 3, Backoff: DefaultBackoff, ErrLog: []ErrEntry{}} + +// DefaultBackoff always returns 1 second +func DefaultBackoff(_ int) time.Duration { + return 1 * time.Second +} + +// ExponentialBackoff returns ever increasing backoffs by a power of 2 +func ExponentialBackoff(i int) time.Duration { + return time.Duration(math.Pow(2, float64(i))) * time.Second +} + +// ExponentialJitterBackoff returns ever increasing backoffs by a power of 2 +// with +/- 0-33% to prevent sychronized reuqests. +func ExponentialJitterBackoff(i int) time.Duration { + return jitter(int(math.Pow(2, float64(i)))) +} + +// LinearBackoff returns increasing durations, each a second longer than the last +func LinearBackoff(i int) time.Duration { + return time.Duration(i) * time.Second +} + +// LinearJitterBackoff returns increasing durations, each a second longer than the last +// with +/- 0-33% to prevent sychronized reuqests. +func LinearJitterBackoff(i int) time.Duration { + return jitter(i) +} + +// jitter keeps the +/- 0-33% logic in one place +func jitter(i int) time.Duration { + ms := i * 1000 + + maxJitter := ms / 3 + + rand.Seed(time.Now().Unix()) + jitter := rand.Intn(maxJitter + 1) + + if rand.Intn(2) == 1 { + ms = ms + jitter + } else { + ms = ms - jitter + } + + // a jitter of 0 messes up the time.Tick chan + if ms <= 0 { + ms = 1 + } + + return time.Duration(ms) * time.Millisecond +} + +// Wait blocks until all pester requests have returned +// Probably not that useful outside of testing. +func (c *Client) Wait() { + c.wg.Wait() +} + +// pester provides all the logic of retries, concurrency, backoff, and logging +func (c *Client) pester(p params) (*http.Response, error) { + resultCh := make(chan result) + multiplexCh := make(chan result) + finishCh := make(chan struct{}) + + // track all requests that go out so we can close the late listener routine that closes late incoming response bodies + totalSentRequests := &sync.WaitGroup{} + totalSentRequests.Add(1) + defer totalSentRequests.Done() + allRequestsBackCh := make(chan struct{}) + go func() { + totalSentRequests.Wait() + close(allRequestsBackCh) + }() + + // GET calls should be idempotent and can make use + // of concurrency. Other verbs can mutate and should not + // make use of the concurrency feature + concurrency := c.Concurrency + if p.verb != "GET" { + concurrency = 1 + } + + c.Lock() + if c.hc == nil { + c.hc = &http.Client{} + c.hc.Transport = c.Transport + c.hc.CheckRedirect = c.CheckRedirect + c.hc.Jar = c.Jar + c.hc.Timeout = c.Timeout + } + c.Unlock() + + // re-create the http client so we can leverage the std lib + httpClient := http.Client{ + Transport: c.hc.Transport, + CheckRedirect: c.hc.CheckRedirect, + Jar: c.hc.Jar, + Timeout: c.hc.Timeout, + } + + // if we have a request body, we need to save it for later + var originalRequestBody []byte + var originalBody []byte + var err error + if p.req != nil && p.req.Body != nil { + originalRequestBody, err = ioutil.ReadAll(p.req.Body) + if err != nil { + return &http.Response{}, errors.New("error reading request body") + } + p.req.Body.Close() + } + if p.body != nil { + originalBody, err = ioutil.ReadAll(p.body) + if err != nil { + return &http.Response{}, errors.New("error reading body") + } + } + + AttemptLimit := c.MaxRetries + if AttemptLimit <= 0 { + AttemptLimit = 1 + } + + for req := 0; req < concurrency; req++ { + c.wg.Add(1) + totalSentRequests.Add(1) + go func(n int, p params) { + defer c.wg.Done() + defer totalSentRequests.Done() + + var err error + for i := 1; i <= AttemptLimit; i++ { + c.wg.Add(1) + defer c.wg.Done() + select { + case <-finishCh: + return + default: + } + resp := &http.Response{} + + // rehydrate the body (it is drained each read) + if len(originalRequestBody) > 0 { + p.req.Body = ioutil.NopCloser(bytes.NewBuffer(originalRequestBody)) + } + if len(originalBody) > 0 { + p.body = bytes.NewBuffer(originalBody) + } + + // route the calls + switch p.method { + case "Do": + resp, err = httpClient.Do(p.req) + case "Get": + resp, err = httpClient.Get(p.url) + case "Head": + resp, err = httpClient.Head(p.url) + case "Post": + resp, err = httpClient.Post(p.url, p.bodyType, p.body) + case "PostForm": + resp, err = httpClient.PostForm(p.url, p.data) + } + + // Early return if we have a valid result + // Only retry (ie, continue the loop) on 5xx status codes + if err == nil && resp.StatusCode < 500 { + multiplexCh <- result{resp: resp, err: err, req: n, retry: i} + return + } + + c.log(ErrEntry{ + Time: time.Now(), + Method: p.method, + Verb: p.verb, + URL: p.url, + Request: n, + Retry: i + 1, // would remove, but would break backward compatibility + Attempt: i, + Err: err, + }) + + // if it is the last iteration, grab the result (which is an error at this point) + if i == AttemptLimit { + multiplexCh <- result{resp: resp, err: err} + return + } + + // if we are retrying, we should close this response body to free the fd + if resp != nil { + resp.Body.Close() + } + + // prevent a 0 from causing the tick to block, pass additional microsecond + <-time.Tick(c.Backoff(i) + 1*time.Microsecond) + } + }(req, p) + } + + // spin off the go routine so it can continually listen in on late results and close the response bodies + go func() { + gotFirstResult := false + for { + select { + case res := <-multiplexCh: + if !gotFirstResult { + gotFirstResult = true + close(finishCh) + resultCh <- res + } else if res.resp != nil { + // we only return one result to the caller; close all other response bodies that come back + // drain the body before close as to not prevent keepalive. see https://gist.github.com/mholt/eba0f2cc96658be0f717 + io.Copy(ioutil.Discard, res.resp.Body) + res.resp.Body.Close() + } + case <-allRequestsBackCh: + // don't leave this goroutine running + return + } + } + }() + + select { + case res := <-resultCh: + c.Lock() + defer c.Unlock() + c.SuccessReqNum = res.req + c.SuccessRetryNum = res.retry + return res.resp, res.err + } +} + +// LogString provides a string representation of the errors the client has seen +func (c *Client) LogString() string { + c.Lock() + defer c.Unlock() + var res string + for _, e := range c.ErrLog { + res += fmt.Sprintf("%d %s [%s] %s request-%d retry-%d error: %s\n", + e.Time.Unix(), e.Method, e.Verb, e.URL, e.Request, e.Retry, e.Err) + } + return res +} + +// LogErrCount is a helper method used primarily for test validation +func (c *Client) LogErrCount() int { + c.Lock() + defer c.Unlock() + return len(c.ErrLog) +} + +// EmbedHTTPClient allows you to extend an existing Pester client with an +// underlying http.Client, such as https://godoc.org/golang.org/x/oauth2/google#DefaultClient +func (c *Client) EmbedHTTPClient(hc *http.Client) { + c.hc = hc +} + +func (c *Client) log(e ErrEntry) { + if c.KeepLog { + c.Lock() + c.ErrLog = append(c.ErrLog, e) + c.Unlock() + } +} + +// Do provides the same functionality as http.Client.Do +func (c *Client) Do(req *http.Request) (resp *http.Response, err error) { + return c.pester(params{method: "Do", req: req, verb: req.Method, url: req.URL.String()}) +} + +// Get provides the same functionality as http.Client.Get +func (c *Client) Get(url string) (resp *http.Response, err error) { + return c.pester(params{method: "Get", url: url, verb: "GET"}) +} + +// Head provides the same functionality as http.Client.Head +func (c *Client) Head(url string) (resp *http.Response, err error) { + return c.pester(params{method: "Head", url: url, verb: "HEAD"}) +} + +// Post provides the same functionality as http.Client.Post +func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *http.Response, err error) { + return c.pester(params{method: "Post", url: url, bodyType: bodyType, body: body, verb: "POST"}) +} + +// PostForm provides the same functionality as http.Client.PostForm +func (c *Client) PostForm(url string, data url.Values) (resp *http.Response, err error) { + return c.pester(params{method: "PostForm", url: url, data: data, verb: "POST"}) +} + +//////////////////////////////////////// +// Provide self-constructing variants // +//////////////////////////////////////// + +// Do provides the same functionality as http.Client.Do and creates its own constructor +func Do(req *http.Request) (resp *http.Response, err error) { + c := New() + return c.Do(req) +} + +// Get provides the same functionality as http.Client.Get and creates its own constructor +func Get(url string) (resp *http.Response, err error) { + c := New() + return c.Get(url) +} + +// Head provides the same functionality as http.Client.Head and creates its own constructor +func Head(url string) (resp *http.Response, err error) { + c := New() + return c.Head(url) +} + +// Post provides the same functionality as http.Client.Post and creates its own constructor +func Post(url string, bodyType string, body io.Reader) (resp *http.Response, err error) { + c := New() + return c.Post(url, bodyType, body) +} + +// PostForm provides the same functionality as http.Client.PostForm and creates its own constructor +func PostForm(url string, data url.Values) (resp *http.Response, err error) { + c := New() + return c.PostForm(url, data) +} diff --git a/vendor/vendor.json b/vendor/vendor.json index ffa1993620..5c41629334 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -574,12 +574,6 @@ "revision": "0b12d6b521d83fc7f755e7cfc1b1fbdd35a01a74", "revisionTime": "2016-02-02T18:50:14Z" }, - { - "checksumSHA1": "bQfc4zGh8WUri465COdQNhJJTw4=", - "path": "github.com/kardianos/govendor", - "revision": "7a0d30eab9e67b3ff60f13cdc483f003c01e04c1", - "revisionTime": "2016-07-09T17:43:04Z" - }, { "checksumSHA1": "QK3MNUdQwUBuznCHZcPijU/3DyI=", "path": "github.com/lib/pq", @@ -670,6 +664,12 @@ "revision": "e64db453f3512cade908163702045e0f31137843", "revisionTime": "2016-06-16T02:49:54Z" }, + { + "checksumSHA1": "8Lm8nsMCFz4+gr9EvQLqK8+w+Ks=", + "path": "github.com/sethgrid/pester", + "revision": "8053687f99650573b28fb75cddf3f295082704d7", + "revisionTime": "2016-04-29T17:20:22Z" + }, { "checksumSHA1": "UADS3X1kxl+/qqeGcmTo0rBiXlQ=", "path": "github.com/ugorji/go/codec", From 9f208ae8f2613672b5d9f8f2cc7afcc7a2b04dfe Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Tue, 12 Jul 2016 16:28:27 -0400 Subject: [PATCH 23/30] Revert 'risky' changes --- audit/format_json.go | 4 ++-- builtin/logical/pki/backend_test.go | 8 ++++---- builtin/logical/pki/crl_util.go | 8 ++++---- builtin/logical/pki/path_fetch.go | 3 +-- builtin/logical/pki/path_root.go | 4 ++-- builtin/logical/transit/backend_test.go | 16 ++++++++-------- builtin/logical/transit/path_keys.go | 3 +-- builtin/logical/transit/policy.go | 8 ++++---- http/sys_health.go | 16 ++++++++-------- http/sys_health_test.go | 8 ++++---- logical/framework/backend.go | 2 +- logical/framework/wal.go | 4 ++-- vault/request_handling.go | 4 ++-- vault/token_store.go | 10 +++++----- vault/token_store_test.go | 13 +++++-------- 15 files changed, 53 insertions(+), 58 deletions(-) diff --git a/audit/format_json.go b/audit/format_json.go index 0e468068af..2850ce2472 100644 --- a/audit/format_json.go +++ b/audit/format_json.go @@ -30,7 +30,7 @@ func (f *FormatJSON) FormatRequest( // Encode! enc := json.NewEncoder(w) return enc.Encode(&JSONRequestEntry{ - Time: time.Now().Format(time.RFC3339), + Time: time.Now().UTC().Format(time.RFC3339), Type: "request", Error: errString, @@ -100,7 +100,7 @@ func (f *FormatJSON) FormatResponse( // Encode! enc := json.NewEncoder(w) return enc.Encode(&JSONResponseEntry{ - Time: time.Now().Format(time.RFC3339), + Time: time.Now().UTC().Format(time.RFC3339), Type: "response", Error: errString, diff --git a/builtin/logical/pki/backend_test.go b/builtin/logical/pki/backend_test.go index 8f7cac62f8..3c2602e78c 100644 --- a/builtin/logical/pki/backend_test.go +++ b/builtin/logical/pki/backend_test.go @@ -958,7 +958,7 @@ func generateCATestingSteps(t *testing.T, caCert, caKey, otherCaCert string, int return fmt.Errorf("got an error: %s", resp.Data["error"].(string)) } - if !(resp.Data["revocation_time"].(time.Time)).IsZero() { + if resp.Data["revocation_time"].(int64) != 0 { return fmt.Errorf("expected a zero revocation time") } @@ -1115,7 +1115,7 @@ func generateCATestingSteps(t *testing.T, caCert, caKey, otherCaCert string, int return fmt.Errorf("got an error: %s", resp.Data["error"].(string)) } - if !(resp.Data["revocation_time"].(time.Time)).IsZero() { + if resp.Data["revocation_time"].(int64) != 0 { return fmt.Errorf("expected a zero revocation time") } @@ -1169,7 +1169,7 @@ func generateCATestingSteps(t *testing.T, caCert, caKey, otherCaCert string, int return fmt.Errorf("got an error: %s", resp.Data["error"].(string)) } - if (resp.Data["revocation_time"].(time.Time)).IsZero() { + if resp.Data["revocation_time"].(int64) == 0 { return fmt.Errorf("expected a non-zero revocation time") } @@ -1187,7 +1187,7 @@ func generateCATestingSteps(t *testing.T, caCert, caKey, otherCaCert string, int return fmt.Errorf("got an error: %s", resp.Data["error"].(string)) } - if (resp.Data["revocation_time"].(time.Time)).IsZero() { + if resp.Data["revocation_time"].(int64) == 0 { return fmt.Errorf("expected a non-zero revocation time") } diff --git a/builtin/logical/pki/crl_util.go b/builtin/logical/pki/crl_util.go index 0de2961508..2dd32b5f3a 100644 --- a/builtin/logical/pki/crl_util.go +++ b/builtin/logical/pki/crl_util.go @@ -12,8 +12,8 @@ import ( ) type revocationInfo struct { - CertificateBytes []byte `json:"certificate_bytes"` - RevocationTime time.Time `json:"revocation_time"` + CertificateBytes []byte `json:"certificate_bytes"` + RevocationTime int64 `json:"revocation_time"` } // Revokes a cert, and tries to be smart about error recovery @@ -87,7 +87,7 @@ func revokeCert(b *backend, req *logical.Request, serial string, fromLease bool) } revInfo.CertificateBytes = certEntry.Value - revInfo.RevocationTime = time.Now() + revInfo.RevocationTime = time.Now().Unix() certEntry, err = logical.StorageEntryJSON("revoked/"+serial, revInfo) if err != nil { @@ -153,7 +153,7 @@ func buildCRL(b *backend, req *logical.Request) error { revokedCerts = append(revokedCerts, pkix.RevokedCertificate{ SerialNumber: revokedCert.SerialNumber, - RevocationTime: revInfo.RevocationTime, + RevocationTime: time.Unix(revInfo.RevocationTime, 0), }) } diff --git a/builtin/logical/pki/path_fetch.go b/builtin/logical/pki/path_fetch.go index 1616a204e5..b37e9ff0fe 100644 --- a/builtin/logical/pki/path_fetch.go +++ b/builtin/logical/pki/path_fetch.go @@ -3,7 +3,6 @@ package pki import ( "encoding/pem" "fmt" - "time" "github.com/hashicorp/vault/helper/certutil" "github.com/hashicorp/vault/logical" @@ -102,7 +101,7 @@ func (b *backend) pathFetchRead(req *logical.Request, data *framework.FieldData) var certEntry, revokedEntry *logical.StorageEntry var funcErr error var certificate []byte - var revocationTime time.Time + var revocationTime int64 response = &logical.Response{ Data: map[string]interface{}{}, } diff --git a/builtin/logical/pki/path_root.go b/builtin/logical/pki/path_root.go index 38de7970d1..b127533dc8 100644 --- a/builtin/logical/pki/path_root.go +++ b/builtin/logical/pki/path_root.go @@ -98,7 +98,7 @@ func (b *backend) pathCAGenerateRoot( resp := &logical.Response{ Data: map[string]interface{}{ - "expiration": parsedBundle.Certificate.NotAfter, + "expiration": int64(parsedBundle.Certificate.NotAfter.Unix()), "serial_number": cb.SerialNumber, }, } @@ -234,7 +234,7 @@ func (b *backend) pathCASignIntermediate( resp := &logical.Response{ Data: map[string]interface{}{ - "expiration": parsedBundle.Certificate.NotAfter, + "expiration": int64(parsedBundle.Certificate.NotAfter.Unix()), "serial_number": cb.SerialNumber, }, } diff --git a/builtin/logical/transit/backend_test.go b/builtin/logical/transit/backend_test.go index f42ea05d4a..49296a0f9e 100644 --- a/builtin/logical/transit/backend_test.go +++ b/builtin/logical/transit/backend_test.go @@ -222,14 +222,14 @@ func testAccStepReadPolicy(t *testing.T, name string, expectNone, derived bool) return nil } var d struct { - Name string `mapstructure:"name"` - Key []byte `mapstructure:"key"` - Keys map[string]time.Time `mapstructure:"keys"` - CipherMode string `mapstructure:"cipher_mode"` - Derived bool `mapstructure:"derived"` - KDFMode string `mapstructure:"kdf_mode"` - DeletionAllowed bool `mapstructure:"deletion_allowed"` - ConvergentEncryption bool `mapstructure:"convergent_encryption"` + Name string `mapstructure:"name"` + Key []byte `mapstructure:"key"` + Keys map[string]int64 `mapstructure:"keys"` + CipherMode string `mapstructure:"cipher_mode"` + Derived bool `mapstructure:"derived"` + KDFMode string `mapstructure:"kdf_mode"` + DeletionAllowed bool `mapstructure:"deletion_allowed"` + ConvergentEncryption bool `mapstructure:"convergent_encryption"` } if err := mapstructure.Decode(resp.Data, &d); err != nil { return err diff --git a/builtin/logical/transit/path_keys.go b/builtin/logical/transit/path_keys.go index 14ce59c4a4..accf00aa68 100644 --- a/builtin/logical/transit/path_keys.go +++ b/builtin/logical/transit/path_keys.go @@ -3,7 +3,6 @@ package transit import ( "fmt" "strconv" - "time" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical/framework" @@ -110,7 +109,7 @@ func (b *backend) pathPolicyRead( resp.Data["convergent_encryption"] = p.ConvergentEncryption } - retKeys := map[string]time.Time{} + retKeys := map[string]int64{} for k, v := range p.Keys { retKeys[strconv.Itoa(k)] = v.CreationTime } diff --git a/builtin/logical/transit/policy.go b/builtin/logical/transit/policy.go index 733e65b013..b9ede365c8 100644 --- a/builtin/logical/transit/policy.go +++ b/builtin/logical/transit/policy.go @@ -25,8 +25,8 @@ const ( // KeyEntry stores the key and metadata type KeyEntry struct { - Key []byte `json:"key"` - CreationTime time.Time `json:"creation_time"` + Key []byte `json:"key"` + CreationTime int64 `json:"creation_time"` } // KeyEntryMap is used to allow JSON marshal/unmarshal @@ -491,7 +491,7 @@ func (p *Policy) rotate(storage logical.Storage) error { p.Keys[p.LatestVersion] = KeyEntry{ Key: newKey, - CreationTime: time.Now(), + CreationTime: time.Now().Unix(), } // This ensures that with new key creations min decryption version is set @@ -510,7 +510,7 @@ func (p *Policy) migrateKeyToKeysMap() { p.Keys = KeyEntryMap{ 1: KeyEntry{ Key: p.Key, - CreationTime: time.Now(), + CreationTime: time.Now().Unix(), }, } p.Key = nil diff --git a/http/sys_health.go b/http/sys_health.go index 04cb960454..2883744c3c 100644 --- a/http/sys_health.go +++ b/http/sys_health.go @@ -115,17 +115,17 @@ func getSysHealth(core *vault.Core, r *http.Request) (int, *HealthResponse, erro // Format the body body := &HealthResponse{ - Initialized: init, - Sealed: sealed, - Standby: standby, - ServerTime: time.Now(), + Initialized: init, + Sealed: sealed, + Standby: standby, + ServerTimeUTC: time.Now().UTC().Unix(), } return code, body, nil } type HealthResponse struct { - Initialized bool `json:"initialized"` - Sealed bool `json:"sealed"` - Standby bool `json:"standby"` - ServerTime time.Time `json:"server_time"` + Initialized bool `json:"initialized"` + Sealed bool `json:"sealed"` + Standby bool `json:"standby"` + ServerTimeUTC int64 `json:"server_time_utc"` } diff --git a/http/sys_health_test.go b/http/sys_health_test.go index 67abe75a18..4447eb0c41 100644 --- a/http/sys_health_test.go +++ b/http/sys_health_test.go @@ -29,7 +29,7 @@ func TestSysHealth_get(t *testing.T) { } testResponseStatus(t, resp, 200) testResponseBody(t, resp, &actual) - expected["server_time"] = actual["server_time"] + expected["server_time_utc"] = actual["server_time_utc"] if !reflect.DeepEqual(actual, expected) { t.Fatalf("bad: expected:%#v\nactual:%#v", expected, actual) } @@ -49,7 +49,7 @@ func TestSysHealth_get(t *testing.T) { } testResponseStatus(t, resp, 500) testResponseBody(t, resp, &actual) - expected["server_time"] = actual["server_time"] + expected["server_time_utc"] = actual["server_time_utc"] if !reflect.DeepEqual(actual, expected) { t.Fatalf("bad: expected:%#v\nactual:%#v", expected, actual) } @@ -78,7 +78,7 @@ func TestSysHealth_customcodes(t *testing.T) { testResponseStatus(t, resp, 202) testResponseBody(t, resp, &actual) - expected["server_time"] = actual["server_time"] + expected["server_time_utc"] = actual["server_time_utc"] if !reflect.DeepEqual(actual, expected) { t.Fatalf("bad: expected:%#v\nactual:%#v", expected, actual) } @@ -102,7 +102,7 @@ func TestSysHealth_customcodes(t *testing.T) { } testResponseStatus(t, resp, 503) testResponseBody(t, resp, &actual) - expected["server_time"] = actual["server_time"] + expected["server_time_utc"] = actual["server_time_utc"] if !reflect.DeepEqual(actual, expected) { t.Fatalf("bad: expected:%#v\nactual:%#v", expected, actual) } diff --git a/logical/framework/backend.go b/logical/framework/backend.go index dda2a05d0d..a4f6dba2ee 100644 --- a/logical/framework/backend.go +++ b/logical/framework/backend.go @@ -466,7 +466,7 @@ func (b *Backend) handleWALRollback( } // If the entry isn't old enough, then don't roll it back - if !entry.CreatedAt.Before(minAge) { + if !time.Unix(entry.CreatedAt, 0).Before(minAge) { continue } diff --git a/logical/framework/wal.go b/logical/framework/wal.go index 306fecbde2..6e6b234bce 100644 --- a/logical/framework/wal.go +++ b/logical/framework/wal.go @@ -15,7 +15,7 @@ type WALEntry struct { ID string `json:"-"` Kind string `json:"type"` Data interface{} `json:"data"` - CreatedAt time.Time `json:"created_at"` + CreatedAt int64 `json:"created_at"` } // PutWAL writes some data to the WAL. @@ -37,7 +37,7 @@ func PutWAL(s logical.Storage, kind string, data interface{}) (string, error) { value, err := json.Marshal(&WALEntry{ Kind: kind, Data: data, - CreatedAt: time.Now(), + CreatedAt: time.Now().UTC().Unix(), }) if err != nil { return "", err diff --git a/vault/request_handling.go b/vault/request_handling.go index 2191c7483f..ae2021e031 100644 --- a/vault/request_handling.go +++ b/vault/request_handling.go @@ -327,7 +327,7 @@ func (c *Core) handleLoginRequest(req *logical.Request) (*logical.Response, *log Policies: auth.Policies, Meta: auth.Metadata, DisplayName: auth.DisplayName, - CreationTime: time.Now(), + CreationTime: time.Now().Unix(), TTL: auth.TTL, } @@ -389,7 +389,7 @@ func (c *Core) wrapInCubbyhole(req *logical.Request, resp *logical.Response) (*l te := TokenEntry{ Path: req.Path, Policies: []string{"response-wrapping"}, - CreationTime: creationTime, + CreationTime: creationTime.Unix(), TTL: resp.WrapInfo.TTL, NumUses: 1, ExplicitMaxTTL: resp.WrapInfo.TTL, diff --git a/vault/token_store.go b/vault/token_store.go index a3253fc27d..cc3da59ece 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -439,7 +439,7 @@ type TokenEntry struct { NumUses int `json:"num_uses" mapstructure:"num_uses" structs:"num_uses"` // Time of token creation - CreationTime time.Time `json:"creation_time" mapstructure:"creation_time" structs:"creation_time"` + CreationTime int64 `json:"creation_time" mapstructure:"creation_time" structs:"creation_time"` // Duration set when token was created TTL time.Duration `json:"ttl" mapstructure:"ttl" structs:"ttl"` @@ -497,7 +497,7 @@ func (ts *TokenStore) rootToken() (*TokenEntry, error) { Policies: []string{"root"}, Path: "auth/token/root", DisplayName: "root", - CreationTime: time.Now(), + CreationTime: time.Now().Unix(), } if err := ts.create(te); err != nil { return nil, err @@ -993,7 +993,7 @@ func (ts *TokenStore) handleCreateCommon( Meta: data.Metadata, DisplayName: "token", NumUses: data.NumUses, - CreationTime: time.Now(), + CreationTime: time.Now().Unix(), } renewable := true @@ -1329,7 +1329,7 @@ func (ts *TokenStore) handleLookup( "display_name": out.DisplayName, "num_uses": out.NumUses, "orphan": false, - "creation_time": out.CreationTime, + "creation_time": int64(out.CreationTime), "creation_ttl": int64(out.TTL.Seconds()), "ttl": int64(0), "role": out.Role, @@ -1348,7 +1348,7 @@ func (ts *TokenStore) handleLookup( } if leaseTimes != nil { if !leaseTimes.LastRenewalTime.IsZero() { - resp.Data["last_renewal_time"] = leaseTimes.LastRenewalTime + resp.Data["last_renewal_time"] = leaseTimes.LastRenewalTime.Unix() } if !leaseTimes.ExpireTime.IsZero() { resp.Data["ttl"] = int64(leaseTimes.ExpireTime.Sub(time.Now().Round(time.Second)).Seconds()) diff --git a/vault/token_store_test.go b/vault/token_store_test.go index dc85cc68a7..dfab0b0d1a 100644 --- a/vault/token_store_test.go +++ b/vault/token_store_test.go @@ -175,7 +175,6 @@ func TestTokenStore_CreateLookup(t *testing.T) { if err != nil { t.Fatalf("err: %v", err) } - ent.CreationTime = out.CreationTime if !reflect.DeepEqual(out, ent) { t.Fatalf("bad: expected:%#v\nactual:%#v", ent, out) } @@ -215,7 +214,6 @@ func TestTokenStore_CreateLookup_ProvidedID(t *testing.T) { if err != nil { t.Fatalf("err: %v", err) } - ent.CreationTime = out.CreationTime if !reflect.DeepEqual(out, ent) { t.Fatalf("bad: expected:%#v\nactual:%#v", ent, out) } @@ -413,7 +411,6 @@ func TestTokenStore_Revoke_Orphan(t *testing.T) { if err != nil { t.Fatalf("err: %v", err) } - ent2.CreationTime = out.CreationTime if !reflect.DeepEqual(out, ent2) { t.Fatalf("bad: expected:%#v\nactual:%#v", ent2, out) } @@ -985,7 +982,7 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) { "explicit_max_ttl": int64(0), } - if (resp.Data["creation_time"].(time.Time)).IsZero() { + if resp.Data["creation_time"].(int64) == 0 { t.Fatalf("creation time was zero") } delete(resp.Data, "creation_time") @@ -1022,7 +1019,7 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) { "renewable": true, } - if (resp.Data["creation_time"].(time.Time)).IsZero() { + if resp.Data["creation_time"].(int64) == 0 { t.Fatalf("creation time was zero") } delete(resp.Data, "creation_time") @@ -1065,7 +1062,7 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) { "renewable": true, } - if (resp.Data["creation_time"].(time.Time)).IsZero() { + if resp.Data["creation_time"].(int64) == 0 { t.Fatalf("creation time was zero") } delete(resp.Data, "creation_time") @@ -1098,7 +1095,7 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) { t.Fatalf("bad: %#v", resp) } - if (resp.Data["last_renewal_time"].(time.Time)).IsZero() { + if resp.Data["last_renewal_time"].(int64) == 0 { t.Fatalf("last_renewal_time was zero") } } @@ -1130,7 +1127,7 @@ func TestTokenStore_HandleRequest_LookupSelf(t *testing.T) { "explicit_max_ttl": int64(0), } - if (resp.Data["creation_time"].(time.Time)).IsZero() { + if resp.Data["creation_time"].(int64) == 0 { t.Fatalf("creation time was zero") } delete(resp.Data, "creation_time") From f200a8568bf3c83aded465f63345f01829af8602 Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Tue, 12 Jul 2016 17:06:28 -0400 Subject: [PATCH 24/30] Set minimum TLS version in all tls.Config objects --- builtin/credential/ldap/path_config.go | 1 + builtin/logical/cassandra/util.go | 1 + helper/certutil/types.go | 1 + physical/consul.go | 1 + 4 files changed, 4 insertions(+) diff --git a/builtin/credential/ldap/path_config.go b/builtin/credential/ldap/path_config.go index bae1550989..19414fc7ad 100644 --- a/builtin/credential/ldap/path_config.go +++ b/builtin/credential/ldap/path_config.go @@ -215,6 +215,7 @@ type ConfigEntry struct { func (c *ConfigEntry) GetTLSConfig(host string) (*tls.Config, error) { tlsConfig := &tls.Config{ + MinVersion: VersionTLS12, ServerName: host, } if c.InsecureTLS { diff --git a/builtin/logical/cassandra/util.go b/builtin/logical/cassandra/util.go index 063b73a0e9..b728452155 100644 --- a/builtin/logical/cassandra/util.go +++ b/builtin/logical/cassandra/util.go @@ -50,6 +50,7 @@ func createSession(cfg *sessionConfig, s logical.Storage) (*gocql.Session, error if cfg.TLS { tlsConfig := &tls.Config{ InsecureSkipVerify: cfg.InsecureTLS, + MinVersion: VersionTLS12, } if len(cfg.Certificate) > 0 || len(cfg.IssuingCA) > 0 { diff --git a/helper/certutil/types.go b/helper/certutil/types.go index 92ae652dd3..29d9a72389 100644 --- a/helper/certutil/types.go +++ b/helper/certutil/types.go @@ -438,6 +438,7 @@ func (p *ParsedCertBundle) GetTLSConfig(usage TLSUsage) (*tls.Config, error) { tlsConfig := &tls.Config{ NextProtos: []string{"http/1.1"}, + MinVersion: VersionTLS12, } if p.Certificate != nil { diff --git a/physical/consul.go b/physical/consul.go index 657bf2f63f..a3c4603004 100644 --- a/physical/consul.go +++ b/physical/consul.go @@ -191,6 +191,7 @@ func setupTLSConfig(conf map[string]string) (*tls.Config, error) { } tlsClientConfig := &tls.Config{ + MinVersion: VersionTLS12, InsecureSkipVerify: insecureSkipVerify, ServerName: serverName[0], } From ee6ba1e85e0f980a3d6411d9d0154e6f6b44a641 Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Tue, 12 Jul 2016 19:32:47 -0400 Subject: [PATCH 25/30] Make 'tls_min_version' configurable --- builtin/credential/ldap/path_config.go | 61 +++++++++++-------- builtin/logical/cassandra/backend.go | 21 ++++--- .../cassandra/path_config_connection.go | 19 +++++- builtin/logical/cassandra/util.go | 13 ++-- command/server/listener.go | 11 +--- helper/certutil/types.go | 2 +- physical/consul.go | 2 +- 7 files changed, 75 insertions(+), 54 deletions(-) diff --git a/builtin/credential/ldap/path_config.go b/builtin/credential/ldap/path_config.go index 19414fc7ad..d34fbfecc7 100644 --- a/builtin/credential/ldap/path_config.go +++ b/builtin/credential/ldap/path_config.go @@ -8,7 +8,9 @@ import ( "net/url" "strings" + "github.com/fatih/structs" "github.com/go-ldap/ldap" + "github.com/hashicorp/vault/helper/tlsutil" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical/framework" ) @@ -71,6 +73,12 @@ func pathConfig(b *backend) *framework.Path { Type: framework.TypeBool, Description: "Issue a StartTLS command after establishing unencrypted connection (optional)", }, + + "tls_min_version": &framework.FieldSchema{ + Type: framework.TypeString, + Default: "tls12", + Description: "Minimum TLS version to use. Accepted values are 'tls10', 'tls11' or 'tls12'. Defaults to 'tls12'", + }, }, Callbacks: map[logical.Operation]framework.OperationFunc{ @@ -110,20 +118,14 @@ func (b *backend) pathConfigRead( return nil, nil } + // Convert the struct into a map + data := structs.New(cfg).Map() + + // Convert the integer representing the TLS version into string of + // the form 'tls10', 'tls11' or 'tls12' + data["tls_min_version"] = tlsutil.TLSReverseLookup[data["tls_min_version"].(uint16)] return &logical.Response{ - Data: map[string]interface{}{ - "url": cfg.Url, - "userdn": cfg.UserDN, - "groupdn": cfg.GroupDN, - "upndomain": cfg.UPNDomain, - "userattr": cfg.UserAttr, - "certificate": cfg.Certificate, - "insecure_tls": cfg.InsecureTLS, - "starttls": cfg.StartTLS, - "binddn": cfg.BindDN, - "bindpass": cfg.BindPassword, - "discoverdn": cfg.DiscoverDN, - }, + Data: data, }, nil } @@ -159,6 +161,14 @@ func (b *backend) pathConfigWrite( if insecureTLS { cfg.InsecureTLS = insecureTLS } + tlsMinVersion := d.Get("tls_min_version").(string) + if tlsMinVersion != "" { + var ok bool + cfg.TLSMinVersion, ok = tlsutil.TLSLookup[tlsMinVersion] + if !ok { + return logical.ErrorResponse("failed to set 'tls_min_version'"), nil + } + } startTLS := d.Get("starttls").(bool) if startTLS { cfg.StartTLS = startTLS @@ -200,22 +210,23 @@ func (b *backend) pathConfigWrite( } type ConfigEntry struct { - Url string - UserDN string - GroupDN string - UPNDomain string - UserAttr string - Certificate string - InsecureTLS bool - StartTLS bool - BindDN string - BindPassword string - DiscoverDN bool + Url string `json:"url" structs:"url" mapstructure:"url"` + UserDN string `json:"userdn" structs:"userdn" mapstructure:"userdn"` + GroupDN string `json:"groupdn" structs:"groupdn" mapstructure:"groupdn"` + UPNDomain string `json:"upndomain" structs:"upndomain" mapstructure:"upndomain"` + UserAttr string `json:"userattr" structs:"userattr" mapstructure:"userattr"` + Certificate string `json:"certificate" structs:"certificate" mapstructure:"certificate"` + InsecureTLS bool `json:"insecure_tls" structs:"insecure_tls" mapstructure:"insecure_tls"` + StartTLS bool `json:"starttls" structs:"starttls" mapstructure:"starttls"` + BindDN string `json:"binddn" structs:"binddn" mapstructure:"binddn"` + BindPassword string `json:"bindpass" structs:"bindpass" mapstructure:"bindpass"` + DiscoverDN bool `json:"discoverdn" structs:"discoverdn" mapstructure:"discoverdn"` + TLSMinVersion uint16 `json:"tls_min_version" structs:"tls_min_version" mapstructure:"tls_min_version"` } func (c *ConfigEntry) GetTLSConfig(host string) (*tls.Config, error) { tlsConfig := &tls.Config{ - MinVersion: VersionTLS12, + MinVersion: c.TLSMinVersion, ServerName: host, } if c.InsecureTLS { diff --git a/builtin/logical/cassandra/backend.go b/builtin/logical/cassandra/backend.go index 0b5d393224..5776c8604e 100644 --- a/builtin/logical/cassandra/backend.go +++ b/builtin/logical/cassandra/backend.go @@ -46,16 +46,17 @@ type backend struct { } type sessionConfig struct { - Hosts string `json:"hosts" structs:"hosts"` - Username string `json:"username" structs:"username"` - Password string `json:"password" structs:"password"` - TLS bool `json:"tls" structs:"tls"` - InsecureTLS bool `json:"insecure_tls" structs:"insecure_tls"` - Certificate string `json:"certificate" structs:"certificate"` - PrivateKey string `json:"private_key" structs:"private_key"` - IssuingCA string `json:"issuing_ca" structs:"issuing_ca"` - ProtocolVersion int `json:"protocol_version" structs:"protocol_version"` - ConnectTimeout int `json:"connect_timeout" structs:"connect_timeout"` + Hosts string `json:"hosts" structs:"hosts" mapstructure:"hosts"` + Username string `json:"username" structs:"username" mapstructure:"username"` + Password string `json:"password" structs:"password" mapstructure:"password"` + TLS bool `json:"tls" structs:"tls" mapstructure:"tls"` + InsecureTLS bool `json:"insecure_tls" structs:"insecure_tls" mapstructure:"insecure_tls"` + Certificate string `json:"certificate" structs:"certificate" mapstructure:"certificate"` + PrivateKey string `json:"private_key" structs:"private_key" mapstructure:"private_key"` + IssuingCA string `json:"issuing_ca" structs:"issuing_ca" mapstructure:"issuing_ca"` + ProtocolVersion int `json:"protocol_version" structs:"protocol_version" mapstructure:"protocol_version"` + ConnectTimeout int `json:"connect_timeout" structs:"connect_timeout" mapstructure:"connect_timeout"` + TLSMinVersion uint16 `json:"tls_min_version" structs:"tls_min_version" mapstructure:"tls_min_version"` } // DB returns the database connection. diff --git a/builtin/logical/cassandra/path_config_connection.go b/builtin/logical/cassandra/path_config_connection.go index a684313e9a..84ae3ef58d 100644 --- a/builtin/logical/cassandra/path_config_connection.go +++ b/builtin/logical/cassandra/path_config_connection.go @@ -5,6 +5,7 @@ import ( "github.com/fatih/structs" "github.com/hashicorp/vault/helper/certutil" + "github.com/hashicorp/vault/helper/tlsutil" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical/framework" ) @@ -40,6 +41,12 @@ set, this is automatically set to true`, effect if a CA certificate is provided`, }, + "tls_min_version": &framework.FieldSchema{ + Type: framework.TypeString, + Default: "tls12", + Description: "Minimum TLS version to use. Accepted values are 'tls10', 'tls11' or 'tls12'. Defaults to 'tls12'", + }, + "pem_bundle": &framework.FieldSchema{ Type: framework.TypeString, Description: `PEM-format, concatenated unencrypted secret key @@ -98,8 +105,9 @@ func (b *backend) pathConnectionRead( config.PrivateKey = "**********" } + d := structs.New(config).Map() return &logical.Response{ - Data: structs.New(config).Map(), + Data: d, }, nil } @@ -128,6 +136,15 @@ func (b *backend) pathConnectionWrite( ConnectTimeout: data.Get("connect_timeout").(int), } + tlsMinVersion := data.Get("tls_min_version").(string) + if tlsMinVersion != "" { + var ok bool + config.TLSMinVersion, ok = tlsutil.TLSLookup[tlsMinVersion] + if !ok { + return logical.ErrorResponse("failed to set 'tls_min_version'"), nil + } + } + if config.InsecureTLS { config.TLS = true } diff --git a/builtin/logical/cassandra/util.go b/builtin/logical/cassandra/util.go index b728452155..08fdb7d223 100644 --- a/builtin/logical/cassandra/util.go +++ b/builtin/logical/cassandra/util.go @@ -48,11 +48,7 @@ func createSession(cfg *sessionConfig, s logical.Storage) (*gocql.Session, error clusterConfig.Timeout = time.Duration(cfg.ConnectTimeout) * time.Second if cfg.TLS { - tlsConfig := &tls.Config{ - InsecureSkipVerify: cfg.InsecureTLS, - MinVersion: VersionTLS12, - } - + var tlsConfig *tls.Config if len(cfg.Certificate) > 0 || len(cfg.IssuingCA) > 0 { if len(cfg.Certificate) > 0 && len(cfg.PrivateKey) == 0 { return nil, fmt.Errorf("Found certificate for TLS authentication but no private key") @@ -65,18 +61,19 @@ func createSession(cfg *sessionConfig, s logical.Storage) (*gocql.Session, error } if len(cfg.IssuingCA) > 0 { certBundle.IssuingCA = cfg.IssuingCA - tlsConfig.InsecureSkipVerify = false } parsedCertBundle, err := certBundle.ToParsedCertBundle() if err != nil { - return nil, fmt.Errorf("Error parsing certificate bundle: %s", err) + return nil, fmt.Errorf("failed to parse certificate bundle: %s", err) } tlsConfig, err = parsedCertBundle.GetTLSConfig(certutil.TLSClient) if err != nil { - return nil, fmt.Errorf("Error getting TLS configuration: %s", err) + return nil, fmt.Errorf("failed to get TLS configuration: %s", err) } + tlsConfig.InsecureSkipVerify = cfg.InsecureTLS + tlsConfig.MinVersion = cfg.TLSMinVersion } clusterConfig.SslOpts = &gocql.SslOptions{ diff --git a/command/server/listener.go b/command/server/listener.go index 051ab2e8b9..7948bda988 100644 --- a/command/server/listener.go +++ b/command/server/listener.go @@ -10,6 +10,8 @@ import ( "net" "strconv" "sync" + + "github.com/hashicorp/vault/helper/tlsutil" ) // ListenerFactory is the factory function to create a listener. @@ -21,13 +23,6 @@ var BuiltinListeners = map[string]ListenerFactory{ "atlas": atlasListenerFactory, } -// tlsLookup maps the tls_min_version configuration to the internal value -var tlsLookup = map[string]uint16{ - "tls10": tls.VersionTLS10, - "tls11": tls.VersionTLS11, - "tls12": tls.VersionTLS12, -} - // NewListener creates a new listener of the given type with the given // configuration. The type is looked up in the BuiltinListeners map. func NewListener(t string, config map[string]string, logger io.Writer) (net.Listener, map[string]string, ReloadFunc, error) { @@ -81,7 +76,7 @@ func listenerWrapTLS( tlsConf := &tls.Config{} tlsConf.GetCertificate = cg.getCertificate tlsConf.NextProtos = []string{"http/1.1"} - tlsConf.MinVersion, ok = tlsLookup[tlsvers] + tlsConf.MinVersion, ok = tlsutil.TLSLookup[tlsvers] if !ok { return nil, nil, nil, fmt.Errorf("'tls_min_version' value %s not supported, please specify one of [tls10,tls11,tls12]", tlsvers) } diff --git a/helper/certutil/types.go b/helper/certutil/types.go index 29d9a72389..6767a8d241 100644 --- a/helper/certutil/types.go +++ b/helper/certutil/types.go @@ -438,7 +438,7 @@ func (p *ParsedCertBundle) GetTLSConfig(usage TLSUsage) (*tls.Config, error) { tlsConfig := &tls.Config{ NextProtos: []string{"http/1.1"}, - MinVersion: VersionTLS12, + MinVersion: tls.VersionTLS12, } if p.Certificate != nil { diff --git a/physical/consul.go b/physical/consul.go index a3c4603004..439760d17d 100644 --- a/physical/consul.go +++ b/physical/consul.go @@ -191,7 +191,7 @@ func setupTLSConfig(conf map[string]string) (*tls.Config, error) { } tlsClientConfig := &tls.Config{ - MinVersion: VersionTLS12, + MinVersion: tls.VersionTLS12, InsecureSkipVerify: insecureSkipVerify, ServerName: serverName[0], } From 150cba24a77b4fbdc5fb5c57da7ca753c112bba1 Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Tue, 12 Jul 2016 19:56:35 -0400 Subject: [PATCH 26/30] Added tls_min_version to consul storage backend --- builtin/credential/ldap/path_config.go | 16 +++++++++------ .../cassandra/path_config_connection.go | 20 +++++++++++++------ physical/consul.go | 14 ++++++++++++- website/source/docs/config/index.html.md | 3 +++ 4 files changed, 40 insertions(+), 13 deletions(-) diff --git a/builtin/credential/ldap/path_config.go b/builtin/credential/ldap/path_config.go index d34fbfecc7..7a3f9f8ba3 100644 --- a/builtin/credential/ldap/path_config.go +++ b/builtin/credential/ldap/path_config.go @@ -124,6 +124,7 @@ func (b *backend) pathConfigRead( // Convert the integer representing the TLS version into string of // the form 'tls10', 'tls11' or 'tls12' data["tls_min_version"] = tlsutil.TLSReverseLookup[data["tls_min_version"].(uint16)] + return &logical.Response{ Data: data, }, nil @@ -162,13 +163,16 @@ func (b *backend) pathConfigWrite( cfg.InsecureTLS = insecureTLS } tlsMinVersion := d.Get("tls_min_version").(string) - if tlsMinVersion != "" { - var ok bool - cfg.TLSMinVersion, ok = tlsutil.TLSLookup[tlsMinVersion] - if !ok { - return logical.ErrorResponse("failed to set 'tls_min_version'"), nil - } + if tlsMinVersion == "" { + return logical.ErrorResponse("failed to get 'tls_min_version' value"), nil } + + var ok bool + cfg.TLSMinVersion, ok = tlsutil.TLSLookup[tlsMinVersion] + if !ok { + return logical.ErrorResponse("invalid 'tls_min_version'"), nil + } + startTLS := d.Get("starttls").(bool) if startTLS { cfg.StartTLS = startTLS diff --git a/builtin/logical/cassandra/path_config_connection.go b/builtin/logical/cassandra/path_config_connection.go index 84ae3ef58d..cc3d594b87 100644 --- a/builtin/logical/cassandra/path_config_connection.go +++ b/builtin/logical/cassandra/path_config_connection.go @@ -105,7 +105,13 @@ func (b *backend) pathConnectionRead( config.PrivateKey = "**********" } + // Convert the struct into a map d := structs.New(config).Map() + + // Convert the integer representing the TLS version into string of + // the form 'tls10', 'tls11' or 'tls12' + d["tls_min_version"] = tlsutil.TLSReverseLookup[d["tls_min_version"].(uint16)] + return &logical.Response{ Data: d, }, nil @@ -137,12 +143,14 @@ func (b *backend) pathConnectionWrite( } tlsMinVersion := data.Get("tls_min_version").(string) - if tlsMinVersion != "" { - var ok bool - config.TLSMinVersion, ok = tlsutil.TLSLookup[tlsMinVersion] - if !ok { - return logical.ErrorResponse("failed to set 'tls_min_version'"), nil - } + if tlsMinVersion == "" { + return logical.ErrorResponse("failed to get 'tls_min_version' value"), nil + } + + var ok bool + config.TLSMinVersion, ok = tlsutil.TLSLookup[tlsMinVersion] + if !ok { + return logical.ErrorResponse("invalid 'tls_min_version'"), nil } if config.InsecureTLS { diff --git a/physical/consul.go b/physical/consul.go index 439760d17d..455b5608b5 100644 --- a/physical/consul.go +++ b/physical/consul.go @@ -20,6 +20,7 @@ import ( "github.com/hashicorp/consul/lib" "github.com/hashicorp/errwrap" "github.com/hashicorp/go-cleanhttp" + "github.com/hashicorp/vault/helper/tlsutil" ) const ( @@ -190,8 +191,19 @@ func setupTLSConfig(conf map[string]string) (*tls.Config, error) { insecureSkipVerify = true } + tlsMinVersionStr, ok := conf["tls_min_version"] + if !ok { + // Set the default value + tlsMinVersionStr = "tls12" + } + + tlsMinVersion, ok := tlsutil.TLSLookup[tlsMinVersionStr] + if !ok { + return nil, fmt.Errorf("invalid 'tls_min_version'") + } + tlsClientConfig := &tls.Config{ - MinVersion: tls.VersionTLS12, + MinVersion: tlsMinVersion, InsecureSkipVerify: insecureSkipVerify, ServerName: serverName[0], } diff --git a/website/source/docs/config/index.html.md b/website/source/docs/config/index.html.md index efe129c0c7..3475b8da96 100644 --- a/website/source/docs/config/index.html.md +++ b/website/source/docs/config/index.html.md @@ -223,6 +223,9 @@ For Consul, the following options are supported: * `tls_skip_verify` (optional) - If non-empty, then TLS host verification will be disabled for Consul communication. Defaults to false. + * `tls_min_version` (optional) - Minimum TLS version to use. Accepted values + are 'tls10', 'tls11' or 'tls12'. Defaults to 'tls12'. + The following settings should be set according to your [Consul encryption settings](https://www.consul.io/docs/agent/encryption.html): From 57117f0e5664a4bfb01b4ec0ca21160e6d626e62 Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Wed, 13 Jul 2016 11:29:17 -0400 Subject: [PATCH 27/30] git add tlsutil --- helper/tlsutil/tls.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 helper/tlsutil/tls.go diff --git a/helper/tlsutil/tls.go b/helper/tlsutil/tls.go new file mode 100644 index 0000000000..28b1fb79ec --- /dev/null +++ b/helper/tlsutil/tls.go @@ -0,0 +1,16 @@ +package tlsutil + +import "crypto/tls" + +// TLSLookup maps the tls_min_version configuration to the internal value +var TLSLookup = map[string]uint16{ + "tls10": tls.VersionTLS10, + "tls11": tls.VersionTLS11, + "tls12": tls.VersionTLS12, +} + +var TLSReverseLookup = map[uint16]string{ + tls.VersionTLS10: "tls10", + tls.VersionTLS11: "tls11", + tls.VersionTLS12: "tls12", +} From 98d56846993cbee444c8a7ca6c4377e9f5b63418 Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Wed, 13 Jul 2016 11:52:26 -0400 Subject: [PATCH 28/30] Address review feedback --- builtin/credential/ldap/path_config.go | 24 +++++++++---------- builtin/logical/cassandra/backend.go | 2 +- .../cassandra/path_config_connection.go | 15 ++++-------- builtin/logical/cassandra/util.go | 13 +++++++--- helper/tlsutil/tls.go | 6 ----- 5 files changed, 26 insertions(+), 34 deletions(-) diff --git a/builtin/credential/ldap/path_config.go b/builtin/credential/ldap/path_config.go index 7a3f9f8ba3..c43dcf8ee1 100644 --- a/builtin/credential/ldap/path_config.go +++ b/builtin/credential/ldap/path_config.go @@ -118,15 +118,8 @@ func (b *backend) pathConfigRead( return nil, nil } - // Convert the struct into a map - data := structs.New(cfg).Map() - - // Convert the integer representing the TLS version into string of - // the form 'tls10', 'tls11' or 'tls12' - data["tls_min_version"] = tlsutil.TLSReverseLookup[data["tls_min_version"].(uint16)] - return &logical.Response{ - Data: data, + Data: structs.New(cfg).Map(), }, nil } @@ -162,13 +155,13 @@ func (b *backend) pathConfigWrite( if insecureTLS { cfg.InsecureTLS = insecureTLS } - tlsMinVersion := d.Get("tls_min_version").(string) - if tlsMinVersion == "" { + cfg.TLSMinVersion = d.Get("tls_min_version").(string) + if cfg.TLSMinVersion == "" { return logical.ErrorResponse("failed to get 'tls_min_version' value"), nil } var ok bool - cfg.TLSMinVersion, ok = tlsutil.TLSLookup[tlsMinVersion] + _, ok = tlsutil.TLSLookup[cfg.TLSMinVersion] if !ok { return logical.ErrorResponse("invalid 'tls_min_version'"), nil } @@ -225,12 +218,17 @@ type ConfigEntry struct { BindDN string `json:"binddn" structs:"binddn" mapstructure:"binddn"` BindPassword string `json:"bindpass" structs:"bindpass" mapstructure:"bindpass"` DiscoverDN bool `json:"discoverdn" structs:"discoverdn" mapstructure:"discoverdn"` - TLSMinVersion uint16 `json:"tls_min_version" structs:"tls_min_version" mapstructure:"tls_min_version"` + TLSMinVersion string `json:"tls_min_version" structs:"tls_min_version" mapstructure:"tls_min_version"` } func (c *ConfigEntry) GetTLSConfig(host string) (*tls.Config, error) { + tlsMinVersion, ok := tlsutil.TLSLookup[c.TLSMinVersion] + if !ok { + return nil, fmt.Errorf("invalid 'tls_min_version' in config") + } + tlsConfig := &tls.Config{ - MinVersion: c.TLSMinVersion, + MinVersion: tlsMinVersion, ServerName: host, } if c.InsecureTLS { diff --git a/builtin/logical/cassandra/backend.go b/builtin/logical/cassandra/backend.go index 5776c8604e..c59d93578b 100644 --- a/builtin/logical/cassandra/backend.go +++ b/builtin/logical/cassandra/backend.go @@ -56,7 +56,7 @@ type sessionConfig struct { IssuingCA string `json:"issuing_ca" structs:"issuing_ca" mapstructure:"issuing_ca"` ProtocolVersion int `json:"protocol_version" structs:"protocol_version" mapstructure:"protocol_version"` ConnectTimeout int `json:"connect_timeout" structs:"connect_timeout" mapstructure:"connect_timeout"` - TLSMinVersion uint16 `json:"tls_min_version" structs:"tls_min_version" mapstructure:"tls_min_version"` + TLSMinVersion string `json:"tls_min_version" structs:"tls_min_version" mapstructure:"tls_min_version"` } // DB returns the database connection. diff --git a/builtin/logical/cassandra/path_config_connection.go b/builtin/logical/cassandra/path_config_connection.go index cc3d594b87..e00587d9a5 100644 --- a/builtin/logical/cassandra/path_config_connection.go +++ b/builtin/logical/cassandra/path_config_connection.go @@ -105,15 +105,8 @@ func (b *backend) pathConnectionRead( config.PrivateKey = "**********" } - // Convert the struct into a map - d := structs.New(config).Map() - - // Convert the integer representing the TLS version into string of - // the form 'tls10', 'tls11' or 'tls12' - d["tls_min_version"] = tlsutil.TLSReverseLookup[d["tls_min_version"].(uint16)] - return &logical.Response{ - Data: d, + Data: structs.New(config).Map(), }, nil } @@ -142,13 +135,13 @@ func (b *backend) pathConnectionWrite( ConnectTimeout: data.Get("connect_timeout").(int), } - tlsMinVersion := data.Get("tls_min_version").(string) - if tlsMinVersion == "" { + config.TLSMinVersion = data.Get("tls_min_version").(string) + if config.TLSMinVersion == "" { return logical.ErrorResponse("failed to get 'tls_min_version' value"), nil } var ok bool - config.TLSMinVersion, ok = tlsutil.TLSLookup[tlsMinVersion] + _, ok = tlsutil.TLSLookup[config.TLSMinVersion] if !ok { return logical.ErrorResponse("invalid 'tls_min_version'"), nil } diff --git a/builtin/logical/cassandra/util.go b/builtin/logical/cassandra/util.go index 08fdb7d223..81b08819fd 100644 --- a/builtin/logical/cassandra/util.go +++ b/builtin/logical/cassandra/util.go @@ -8,6 +8,7 @@ import ( "github.com/gocql/gocql" "github.com/hashicorp/vault/helper/certutil" + "github.com/hashicorp/vault/helper/tlsutil" "github.com/hashicorp/vault/logical" ) @@ -69,11 +70,17 @@ func createSession(cfg *sessionConfig, s logical.Storage) (*gocql.Session, error } tlsConfig, err = parsedCertBundle.GetTLSConfig(certutil.TLSClient) - if err != nil { - return nil, fmt.Errorf("failed to get TLS configuration: %s", err) + if err != nil || tlsConfig == nil { + return nil, fmt.Errorf("failed to get TLS configuration: tlsConfig:%#v err:%v", tlsConfig, err) } tlsConfig.InsecureSkipVerify = cfg.InsecureTLS - tlsConfig.MinVersion = cfg.TLSMinVersion + + var ok bool + tlsConfig.MinVersion, ok = tlsutil.TLSLookup[cfg.TLSMinVersion] + if !ok { + return nil, fmt.Errorf("invalid 'tls_min_version' in config") + } + } clusterConfig.SslOpts = &gocql.SslOptions{ diff --git a/helper/tlsutil/tls.go b/helper/tlsutil/tls.go index 28b1fb79ec..89b943b66e 100644 --- a/helper/tlsutil/tls.go +++ b/helper/tlsutil/tls.go @@ -8,9 +8,3 @@ var TLSLookup = map[string]uint16{ "tls11": tls.VersionTLS11, "tls12": tls.VersionTLS12, } - -var TLSReverseLookup = map[uint16]string{ - tls.VersionTLS10: "tls10", - tls.VersionTLS11: "tls11", - tls.VersionTLS12: "tls12", -} From 6977bdd4901d211582a9d1e4e07f7d2efb1d3b05 Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Wed, 13 Jul 2016 12:32:31 -0400 Subject: [PATCH 29/30] Handled upgrade path for TLSMinVersion --- builtin/credential/ldap/path_config.go | 15 +++++++++------ builtin/logical/cassandra/util.go | 15 ++++++++++----- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/builtin/credential/ldap/path_config.go b/builtin/credential/ldap/path_config.go index c43dcf8ee1..e79a095e87 100644 --- a/builtin/credential/ldap/path_config.go +++ b/builtin/credential/ldap/path_config.go @@ -222,15 +222,18 @@ type ConfigEntry struct { } func (c *ConfigEntry) GetTLSConfig(host string) (*tls.Config, error) { - tlsMinVersion, ok := tlsutil.TLSLookup[c.TLSMinVersion] - if !ok { - return nil, fmt.Errorf("invalid 'tls_min_version' in config") - } - tlsConfig := &tls.Config{ - MinVersion: tlsMinVersion, ServerName: host, } + + if c.TLSMinVersion != "" { + tlsMinVersion, ok := tlsutil.TLSLookup[c.TLSMinVersion] + if !ok { + return nil, fmt.Errorf("invalid 'tls_min_version' in config") + } + tlsConfig.MinVersion = tlsMinVersion + } + if c.InsecureTLS { tlsConfig.InsecureSkipVerify = true } diff --git a/builtin/logical/cassandra/util.go b/builtin/logical/cassandra/util.go index 81b08819fd..de6f15293e 100644 --- a/builtin/logical/cassandra/util.go +++ b/builtin/logical/cassandra/util.go @@ -75,12 +75,17 @@ func createSession(cfg *sessionConfig, s logical.Storage) (*gocql.Session, error } tlsConfig.InsecureSkipVerify = cfg.InsecureTLS - var ok bool - tlsConfig.MinVersion, ok = tlsutil.TLSLookup[cfg.TLSMinVersion] - if !ok { - return nil, fmt.Errorf("invalid 'tls_min_version' in config") + if cfg.TLSMinVersion != "" { + var ok bool + tlsConfig.MinVersion, ok = tlsutil.TLSLookup[cfg.TLSMinVersion] + if !ok { + return nil, fmt.Errorf("invalid 'tls_min_version' in config") + } + } else { + // MinVersion was not being set earlier. Reset it to + // zero to gracefully handle upgrades. + tlsConfig.MinVersion = 0 } - } clusterConfig.SslOpts = &gocql.SslOptions{ From cfe7910714f33b126e35e31791dea066e54297b7 Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Wed, 13 Jul 2016 17:17:04 -0400 Subject: [PATCH 30/30] changelog++ --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 973fe6d092..a98878301c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,8 @@ IMPROVEMENTS: configuration [GH-1581] * secret/mssql,mysql,postgresql: Reading of connection settings is supported in all the sql backends [GH-1515] + * credential/ldap, secret/cassandra, physical/consul: Clients with `tls.Config` + will have `MinVersion` set to TLS 1.2 by default. BUG FIXES: