Merge remote-tracking branch 'origin/master' into aws-cred-chain

This commit is contained in:
Jeff Mitchell 2016-05-05 10:31:12 -04:00
commit 98d7f52ef2
13 changed files with 199 additions and 60 deletions

View file

@ -70,6 +70,7 @@ BUG FIXES:
* command/various: Tell the JSON decoder to not convert all numbers to floats;
fixes some various places where numbers were showing up in scientific
notation
* core: Properly persist mount-tuned TTLs for auth backends [GH-1371]
* core: Don't accidentally crosswire SIGINT to the reload handler [GH-1372]
* credential/github: Make organization comparison case-insensitive during
login [GH-1359]

View file

@ -101,6 +101,15 @@ func connectionState(t *testing.T, serverCAPath, serverCertPath, serverKeyPath,
return connState
}
func failOnError(t *testing.T, resp *logical.Response, err error) {
if resp != nil && resp.IsError() {
t.Fatalf("error returned in response: %s", resp.Data["error"])
}
if err != nil {
t.Fatal(err)
}
}
func TestBackend_CRLs(t *testing.T) {
config := logical.TestBackendConfig()
storage := &logical.InmemStorage{}
@ -130,10 +139,8 @@ func TestBackend_CRLs(t *testing.T) {
Data: certData,
}
_, err = b.HandleRequest(certReq)
if err != nil {
t.Fatal(err)
}
resp, err := b.HandleRequest(certReq)
failOnError(t, resp, err)
// Connection state is presenting the client CA cert and its key.
// This is exactly what is registered at the backend.
@ -146,13 +153,8 @@ func TestBackend_CRLs(t *testing.T) {
ConnState: &connState,
},
}
resp, err := b.HandleRequest(loginReq)
if err != nil {
t.Fatal(err)
}
if resp == nil || resp.IsError() {
t.Fatalf("failed to login")
}
resp, err = b.HandleRequest(loginReq)
failOnError(t, resp, err)
// Now, without changing the registered client CA cert, present from
// the client side, a cert issued using the registered CA.
@ -161,12 +163,7 @@ func TestBackend_CRLs(t *testing.T) {
// Attempt login with the updated connection
resp, err = b.HandleRequest(loginReq)
if err != nil {
t.Fatal(err)
}
if resp == nil || resp.IsError() {
t.Fatalf("failed to login")
}
failOnError(t, resp, err)
// Register a CRL containing the issued client certificate used above.
issuedCRL, err := ioutil.ReadFile(testIssuedCertCRL)
@ -183,10 +180,8 @@ func TestBackend_CRLs(t *testing.T) {
Path: "crls/issuedcrl",
Data: crlData,
}
_, err = b.HandleRequest(crlReq)
if err != nil {
t.Fatal(err)
}
resp, err = b.HandleRequest(crlReq)
failOnError(t, resp, err)
// Attempt login with the revoked certificate.
resp, err = b.HandleRequest(loginReq)
@ -203,10 +198,8 @@ func TestBackend_CRLs(t *testing.T) {
t.Fatal(err)
}
certData["certificate"] = clientCA2
_, err = b.HandleRequest(certReq)
if err != nil {
t.Fatal(err)
}
resp, err = b.HandleRequest(certReq)
failOnError(t, resp, err)
// Test login using a different client CA cert pair.
connState = connectionState(t, serverCAPath, serverCertPath, serverKeyPath, testRootCACertPath2, testRootCAKeyPath2)
@ -214,12 +207,7 @@ func TestBackend_CRLs(t *testing.T) {
// Attempt login with the updated connection
resp, err = b.HandleRequest(loginReq)
if err != nil {
t.Fatal(err)
}
if resp == nil || resp.IsError() {
t.Fatalf("failed to login")
}
failOnError(t, resp, err)
// Register a CRL containing the root CA certificate used above.
rootCRL, err := ioutil.ReadFile(testRootCertCRL)
@ -227,10 +215,8 @@ func TestBackend_CRLs(t *testing.T) {
t.Fatal(err)
}
crlData["crl"] = rootCRL
_, err = b.HandleRequest(crlReq)
if err != nil {
t.Fatal(err)
}
resp, err = b.HandleRequest(crlReq)
failOnError(t, resp, err)
// Attempt login with the same connection state but with the CRL registered
resp, err = b.HandleRequest(loginReq)

View file

@ -0,0 +1,67 @@
vault mount pki
vault mount-tune -max-lease-ttl=438000h pki
vault write pki/root/generate/exported common_name=myvault.com ttl=438000h ip_sans=127.0.0.1
vi cacert.pem
vi cakey.pem
vaultcert.hcl
backend "inmem" {
}
disable_mlock = true
default_lease_ttl = "700h"
max_lease_ttl = "720h"
listener "tcp" {
address = "127.0.0.1:8200"
tls_cert_file = "./cacert.pem"
tls_key_file = "./cakey.pem"
}
========================================
vault mount pki
vault mount-tune -max-lease-ttl=438000h pki
vault write pki/root/generate/exported common_name=myvault.com ttl=438000h max_ttl=438000h ip_sans=127.0.0.1
vi testcacert1.pem
vi testcakey1.pem
vi testcaserial1
vault write pki/config/urls issuing_certificates="http://127.0.0.1:8200/v1/pki/ca" crl_distribution_points="http://127.0.0.1:8200/v1/pki/crl"
vault write pki/roles/myvault-dot-com allowed_domains=myvault.com allow_subdomains=true ttl=437999h max_ttl=438000h allow_ip_sans=true
vault write pki/issue/myvault-dot-com common_name=cert.myvault.com format=pem ip_sans=127.0.0.1
vi testissuedserial1
vault write pki/issue/myvault-dot-com common_name=cert.myvault.com format=pem ip_sans=127.0.0.1
vi testissuedcert2.pem
vi testissuedkey2.pem
vi testissuedserial2
vault write pki/issue/myvault-dot-com common_name=cert.myvault.com format=pem ip_sans=127.0.0.1
vi testissuedserial3
vault write pki/issue/myvault-dot-com common_name=cert.myvault.com format=pem ip_sans=127.0.0.1
vi testissuedcert4.pem
vi testissuedkey4.pem
vi testissuedserial4
vault write pki/issue/myvault-dot-com common_name=cert.myvault.com format=pem ip_sans=127.0.0.1
vi testissuedserial5
vault write pki/revoke serial_number=$(cat testissuedserial2)
vault write pki/revoke serial_number=$(cat testissuedserial4)
curl -XGET "http://127.0.0.1:8200/v1/pki/crl/pem" -H "x-vault-token:123" > issuedcertcrl
openssl crl -in issuedcertcrl -noout -text
========================================
export VAULT_ADDR='http://127.0.0.1:8200'
vault mount pki
vault mount-tune -max-lease-ttl=438000h pki
vault write pki/root/generate/exported common_name=myvault.com ttl=438000h ip_sans=127.0.0.1
vi testcacert2.pem
vi testcakey2.pem
vi testcaserial2
vi testcacert2leaseid
vault write pki/config/urls issuing_certificates="http://127.0.0.1:8200/v1/pki/ca" crl_distribution_points="http://127.0.0.1:8200/v1/pki/crl"
vault revoke $(cat testcacert2leaseid)
curl -XGET "http://127.0.0.1:8200/v1/pki/crl/pem" -H "x-vault-token:123" > cacert2crl
openssl crl -in cacert2crl -noout -text

View file

@ -46,7 +46,7 @@ func (b *backend) pathLogin(
return nil, err
}
ttl, _, err := b.SanitizeTTL(config.TTL.String(), config.MaxTTL.String())
ttl, _, err := b.SanitizeTTLStr(config.TTL.String(), config.MaxTTL.String())
if err != nil {
return logical.ErrorResponse(fmt.Sprintf("[ERR]:%s", err)), nil
}

View file

@ -176,7 +176,7 @@ func (b *backend) userCreateUpdate(req *logical.Request, d *framework.FieldData)
maxTTLStr = maxTTLStrRaw.(string)
}
userEntry.TTL, userEntry.MaxTTL, err = b.SanitizeTTL(ttlStr, maxTTLStr)
userEntry.TTL, userEntry.MaxTTL, err = b.SanitizeTTLStr(ttlStr, maxTTLStr)
if err != nil {
return logical.ErrorResponse(fmt.Sprintf("err: %s", err)), nil
}

View file

@ -92,8 +92,8 @@ func (c *InitCommand) Run(args []string) int {
"\n"+
"Recovery key initialized with %d keys and a key threshold of %d. Please\n"+
"securely distribute the above keys.",
shares,
threshold,
recoveryShares,
recoveryThreshold,
))
}

View file

@ -3,13 +3,29 @@ package policyutil
import (
"sort"
"strings"
"github.com/hashicorp/vault/helper/strutil"
)
func ParsePolicies(policiesRaw string) []string {
if policiesRaw == "" {
return []string{"default"}
}
policies := strings.Split(policiesRaw, ",")
return SanitizePolicies(policies)
}
func SanitizePolicies(policies []string) []string {
defaultFound := false
for i, p := range policies {
policies[i] = strings.TrimSpace(p)
policies[i] = strings.ToLower(strings.TrimSpace(p))
// Eliminate unnamed policies.
if policies[i] == "" {
continue
}
// If 'root' policy is present, ignore all other policies.
if policies[i] == "root" {
policies = []string{"root"}
@ -26,10 +42,7 @@ func ParsePolicies(policiesRaw string) []string {
policies = append(policies, "default")
}
// Sort to make the computations on policies consistent.
sort.Strings(policies)
return policies
return strutil.RemoveDuplicates(policies)
}
// ComparePolicies checks whether the given policy sets are equivalent, as in,

View file

@ -1,5 +1,10 @@
package strutil
import (
"sort"
"strings"
)
// StrListContains looks for a string in a list of strings.
func StrListContains(haystack []string, needle string) bool {
for _, item := range haystack {
@ -20,3 +25,35 @@ func StrListSubset(super, sub []string) bool {
}
return true
}
// Parses a comma separated list of strings into a slice of strings.
// The return slice will be sorted and will not contain duplicate or
// empty items. The values will be converted to lower case.
func ParseStrings(input string) []string {
var parsed []string
if input == "" {
// Don't return nil
return parsed
}
return RemoveDuplicates(strings.Split(input, ","))
}
// Removes duplicate and empty elements from a slice of strings.
// This also converts the items in the slice to lower case and
// returns a sorted slice.
func RemoveDuplicates(items []string) []string {
itemsMap := map[string]bool{}
for _, item := range items {
item = strings.ToLower(strings.TrimSpace(item))
if item == "" {
continue
}
itemsMap[item] = true
}
items = []string{}
for item, _ := range itemsMap {
items = append(items, item)
}
sort.Strings(items)
return items
}

View file

@ -225,8 +225,7 @@ func (b *Backend) System() logical.SystemView {
// compares those with the SystemView values. If they are empty a value of 0 is
// set, which will cause initial secret or LeaseExtend operations to use the
// mount/system defaults. If they are set, their boundaries are validated.
func (b *Backend) SanitizeTTL(ttlStr, maxTTLStr string) (ttl, maxTTL time.Duration, err error) {
sysMaxTTL := b.System().MaxLeaseTTL()
func (b *Backend) SanitizeTTLStr(ttlStr, maxTTLStr string) (ttl, maxTTL time.Duration, err error) {
if len(ttlStr) == 0 || ttlStr == "0" {
ttl = 0
} else {
@ -234,10 +233,8 @@ func (b *Backend) SanitizeTTL(ttlStr, maxTTLStr string) (ttl, maxTTL time.Durati
if err != nil {
return 0, 0, fmt.Errorf("Invalid ttl: %s", err)
}
if ttl > sysMaxTTL {
return 0, 0, fmt.Errorf("\"ttl\" value must be less than allowed max lease TTL value '%s'", sysMaxTTL.String())
}
}
if len(maxTTLStr) == 0 || maxTTLStr == "0" {
maxTTL = 0
} else {
@ -245,14 +242,26 @@ func (b *Backend) SanitizeTTL(ttlStr, maxTTLStr string) (ttl, maxTTL time.Durati
if err != nil {
return 0, 0, fmt.Errorf("Invalid max_ttl: %s", err)
}
if maxTTL > sysMaxTTL {
return 0, 0, fmt.Errorf("\"max_ttl\" value must be less than allowed max lease TTL value '%s'", sysMaxTTL.String())
}
}
ttl, maxTTL, err = b.SanitizeTTL(ttl, maxTTL)
return
}
// Caps the boundaries of ttl and max_ttl values to the backend mount's max_ttl value.
func (b *Backend) SanitizeTTL(ttl, maxTTL time.Duration) (time.Duration, time.Duration, error) {
sysMaxTTL := b.System().MaxLeaseTTL()
if ttl > sysMaxTTL {
return 0, 0, fmt.Errorf("\"ttl\" value must be less than allowed max lease TTL value '%s'", sysMaxTTL.String())
}
if maxTTL > sysMaxTTL {
return 0, 0, fmt.Errorf("\"max_ttl\" value must be less than allowed max lease TTL value '%s'", sysMaxTTL.String())
}
if ttl > maxTTL && maxTTL != 0 {
ttl = maxTTL
}
return
return ttl, maxTTL, nil
}
// Route looks up the path that would be used for a given path string.

View file

@ -3,6 +3,7 @@ package vault
import (
"fmt"
"strings"
"sync"
"time"
"github.com/hashicorp/vault/logical"
@ -845,6 +846,14 @@ func (b *SystemBackend) handleMountTuneWrite(
return handleError(err)
}
var lock *sync.RWMutex
switch {
case strings.HasPrefix(path, "auth/"):
lock = &b.Core.authLock
default:
lock = &b.Core.mountsLock
}
// Timing configuration parameters
{
var newDefault, newMax *time.Duration
@ -877,8 +886,9 @@ func (b *SystemBackend) handleMountTuneWrite(
}
if newDefault != nil || newMax != nil {
b.Core.mountsLock.Lock()
defer b.Core.mountsLock.Unlock()
lock.Lock()
defer lock.Unlock()
if err := b.tuneMountTTLs(path, &mountEntry.Config, newDefault, newMax); err != nil {
b.Backend.Logger().Printf("[ERR] sys: tune of path '%s' failed: %v", path, err)
return handleError(err)

View file

@ -1,8 +1,8 @@
package vault
import (
"errors"
"fmt"
"strings"
"time"
)
@ -51,6 +51,9 @@ func (b *SystemBackend) tuneMountTTLs(path string, meConfig *MountConfig, newDef
}
}
origMax := meConfig.MaxLeaseTTL
origDefault := meConfig.DefaultLeaseTTL
if newMax != nil {
meConfig.MaxLeaseTTL = *newMax
}
@ -59,8 +62,17 @@ func (b *SystemBackend) tuneMountTTLs(path string, meConfig *MountConfig, newDef
}
// Update the mount table
if err := b.Core.persistMounts(b.Core.mounts); err != nil {
return errors.New("failed to update mount table")
var err error
switch {
case strings.HasPrefix(path, "auth/"):
err = b.Core.persistAuth(b.Core.auth)
default:
err = b.Core.persistMounts(b.Core.mounts)
}
if err != nil {
meConfig.MaxLeaseTTL = origMax
meConfig.DefaultLeaseTTL = origDefault
return fmt.Errorf("failed to update mount table, rolling back TTL changes")
}
b.Core.logger.Printf("[INFO] core: tuned '%s'", path)

View file

@ -599,8 +599,9 @@ of the header should be "X-Vault-Token" and the value should be the token.
If set, tokens created against this role will <i>not</i> have a maximum
lifetime. Instead, they will have a fixed TTL that is refreshed with
each renewal. So long as they continue to be renewed, they will never
expire. The parameter is an integer duration of seconds or a duration
string (e.g. `"72h"`).
expire. The parameter is an integer duration of seconds. Tokens issued
track updates to the role value; the new period takes effect upon next
renew.
</li>
<li>
<span class="param">path_suffix</span>

View file

@ -59,3 +59,6 @@ These libraries are provided by the community.
* [HVAC](https://github.com/ianunruh/hvac)
* `pip install hvac`
### Scala
* [scala-vault](https://github.com/janstenpickle/scala-vault)