Merge branch 'master-oss' into aws-auth-backend
This commit is contained in:
commit
4600ca8073
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
84
helper/awsutil/generate_credentials.go
Normal file
84
helper/awsutil/generate_credentials.go
Normal 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
|
||||
}
|
|
@ -3,18 +3,29 @@ package policyutil
|
|||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/vault/helper/strutil"
|
||||
)
|
||||
|
||||
func ParsePolicies(policiesRaw string) []string {
|
||||
var policies []string
|
||||
|
||||
if policiesRaw != "" {
|
||||
policies = strings.Split(policiesRaw, ",")
|
||||
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.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"}
|
||||
|
@ -31,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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue