Merge branch 'master-oss' into cubbyhole-the-world

This commit is contained in:
Jeff Mitchell 2016-05-05 20:45:36 -04:00
commit 75dbbff1a6
10 changed files with 203 additions and 46 deletions

View File

@ -56,6 +56,8 @@ IMPROVEMENTS:
* credential/userpass: Add list support for users [GH-911]
* credential/userpass: Remove user configuration paths from requiring sudo, in
favor of normal ACL mechanisms [GH-1312]
* secret/aws: Use chain credentials to allow environment/EC2 instance/shared
providers [GH-307]
* secret/pki: Added `exclude_cn_from_sans` field to prevent adding the CN to
DNS or Email Subject Alternate Names [GH-1220]
* sys/capabilities: Enforce ACL checks for requests that query the capabilities

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

@ -4,34 +4,46 @@ import (
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/iam"
"github.com/aws/aws-sdk-go/service/sts"
"github.com/hashicorp/go-cleanhttp"
"github.com/hashicorp/vault/helper/awsutil"
"github.com/hashicorp/vault/logical"
)
func getRootConfig(s logical.Storage) (*aws.Config, error) {
credsConfig := &awsutil.CredentialsConfig{}
entry, err := s.Get("config/root")
if err != nil {
return nil, err
}
if entry == nil {
return nil, fmt.Errorf(
"root credentials haven't been configured. Please configure\n" +
"them at the 'config/root' endpoint")
if entry != nil {
var config rootConfig
if err := entry.DecodeJSON(&config); err != nil {
return nil, fmt.Errorf("error reading root configuration: %s", err)
}
credsConfig.AccessKey = config.AccessKey
credsConfig.SecretKey = config.SecretKey
credsConfig.Region = config.Region
}
var config rootConfig
if err := entry.DecodeJSON(&config); err != nil {
return nil, fmt.Errorf("error reading root configuration: %s", err)
if credsConfig.Region == "" {
credsConfig.Region = "us-east-1"
}
credsConfig.HTTPClient = cleanhttp.DefaultClient()
creds, err := credsConfig.GenerateCredentialChain()
if err != nil {
return nil, err
}
creds := credentials.NewStaticCredentials(config.AccessKey, config.SecretKey, "")
return &aws.Config{
Credentials: creds,
Region: aws.String(config.Region),
Region: aws.String(credsConfig.Region),
HTTPClient: cleanhttp.DefaultClient(),
}, nil
}

View File

@ -0,0 +1,84 @@
package awsutil
import (
"fmt"
"net/http"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
"github.com/aws/aws-sdk-go/aws/ec2metadata"
"github.com/aws/aws-sdk-go/aws/session"
)
type CredentialsConfig struct {
// The access key if static credentials are being used
AccessKey string
// The secret key if static credentials are being used
SecretKey string
// The session token if it is being used
SessionToken string
// If specified, the region will be provided to the config of the
// EC2RoleProvider's client. This may be useful if you want to e.g. reuse
// the client elsewhere.
Region string
// The filename for the shared credentials provider, if being used
Filename string
// The profile for the shared credentials provider, if being used
Profile string
// The http.Client to use, or nil for the client to use its default
HTTPClient *http.Client
}
func (c *CredentialsConfig) GenerateCredentialChain() (*credentials.Credentials, error) {
var providers []credentials.Provider
switch {
case c.AccessKey != "" && c.SecretKey != "":
// Add the static credential provider
providers = append(providers, &credentials.StaticProvider{
Value: credentials.Value{
AccessKeyID: c.AccessKey,
SecretAccessKey: c.SecretKey,
SessionToken: c.SessionToken,
}})
case c.AccessKey == "" && c.AccessKey == "":
// Attempt to get credentials from the IAM instance role below
default: // Have one or the other but not both and not neither
return nil, fmt.Errorf(
"static AWS client credentials haven't been properly configured (the access key or secret key were provided but not both)")
}
// Add the environment credential provider
providers = append(providers, &credentials.EnvProvider{})
// Add the shared credentials provider
providers = append(providers, &credentials.SharedCredentialsProvider{
Filename: c.Filename,
Profile: c.Profile,
})
// Add the instance metadata role provider
providers = append(providers, &ec2rolecreds.EC2RoleProvider{
Client: ec2metadata.New(session.New(&aws.Config{
Region: aws.String(c.Region),
HTTPClient: c.HTTPClient,
})),
ExpiryWindow: 15,
})
// Create the credentials required to access the API.
creds := credentials.NewChainCredentials(providers)
if creds == nil {
return nil, fmt.Errorf("could not compile valid credential providers from static config, environemnt, shared, or instance metadata")
}
return creds, nil
}

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

@ -13,11 +13,9 @@ import (
"github.com/armon/go-metrics"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
"github.com/aws/aws-sdk-go/aws/ec2metadata"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/hashicorp/vault/helper/awsutil"
)
// S3Backend is a physical backend that stores data
@ -41,17 +39,17 @@ func newS3Backend(conf map[string]string, logger *log.Logger) (Backend, error) {
}
}
access_key, ok := conf["access_key"]
accessKey, ok := conf["access_key"]
if !ok {
access_key = ""
accessKey = ""
}
secret_key, ok := conf["secret_key"]
secretKey, ok := conf["secret_key"]
if !ok {
secret_key = ""
secretKey = ""
}
session_token, ok := conf["session_token"]
sessionToken, ok := conf["session_token"]
if !ok {
session_token = ""
sessionToken = ""
}
endpoint := os.Getenv("AWS_S3_ENDPOINT")
if endpoint == "" {
@ -65,16 +63,15 @@ func newS3Backend(conf map[string]string, logger *log.Logger) (Backend, error) {
}
}
creds := credentials.NewChainCredentials([]credentials.Provider{
&credentials.StaticProvider{Value: credentials.Value{
AccessKeyID: access_key,
SecretAccessKey: secret_key,
SessionToken: session_token,
}},
&credentials.EnvProvider{},
&credentials.SharedCredentialsProvider{Filename: "", Profile: ""},
&ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(session.New())},
})
credsConfig := &awsutil.CredentialsConfig{
AccessKey: accessKey,
SecretKey: secretKey,
SessionToken: sessionToken,
}
creds, err := credsConfig.GenerateCredentialChain()
if err != nil {
return nil, err
}
s3conn := s3.New(session.New(&aws.Config{
Credentials: creds,
@ -82,7 +79,7 @@ func newS3Backend(conf map[string]string, logger *log.Logger) (Backend, error) {
Region: aws.String(region),
}))
_, err := s3conn.HeadBucket(&s3.HeadBucketInput{Bucket: &bucket})
_, err = s3conn.HeadBucket(&s3.HeadBucketInput{Bucket: &bucket})
if err != nil {
return nil, fmt.Errorf("unable to access bucket '%s': %v", bucket, err)
}

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)