Merge branch 'master-oss' into acl-parameters-permission

This commit is contained in:
Jeff Mitchell 2017-02-15 20:37:58 -05:00
commit e2b7d43e01
29 changed files with 352 additions and 127 deletions

View file

@ -5,6 +5,16 @@ IMPROVEMENTS:
* auth/ldap: Use the value of the `LOGNAME` or `USER` env vars for the
username if not explicitly set on the command line when authenticating
[GH-2154]
* audit: Support adding a configurable prefix (such as `@cee`) before each
line [GH-2359]
BUG FIXES:
* auth/aws-ec2: Return role period in seconds and not nanoseconds [GH-2374]
* auth/okta: Fix panic if user had no local groups and/or policies set
[GH-2367]
* command/server: Fix parsing of redirect address when port is not mentioned
[GH-2354]
## 0.6.5 (February 7th, 2017)

View file

@ -8,13 +8,22 @@ import (
// JSONFormatWriter is an AuditFormatWriter implementation that structures data into
// a JSON format.
type JSONFormatWriter struct{}
type JSONFormatWriter struct {
Prefix string
}
func (f *JSONFormatWriter) WriteRequest(w io.Writer, req *AuditRequestEntry) error {
if req == nil {
return fmt.Errorf("request entry was nil, cannot encode")
}
if len(f.Prefix) > 0 {
_, err := w.Write([]byte(f.Prefix))
if err != nil {
return err
}
}
enc := json.NewEncoder(w)
return enc.Encode(req)
}
@ -24,6 +33,13 @@ func (f *JSONFormatWriter) WriteResponse(w io.Writer, resp *AuditResponseEntry)
return fmt.Errorf("response entry was nil, cannot encode")
}
if len(f.Prefix) > 0 {
_, err := w.Write([]byte(f.Prefix))
if err != nil {
return err
}
}
enc := json.NewEncoder(w)
return enc.Encode(resp)
}

View file

@ -19,6 +19,7 @@ func TestFormatJSON_formatRequest(t *testing.T) {
Auth *logical.Auth
Req *logical.Request
Err error
Prefix string
Result string
}{
"auth, request": {
@ -37,6 +38,26 @@ func TestFormatJSON_formatRequest(t *testing.T) {
},
},
errors.New("this is an error"),
"",
testFormatJSONReqBasicStr,
},
"auth, request with prefix": {
&logical.Auth{ClientToken: "foo", Policies: []string{"root"}},
&logical.Request{
Operation: logical.UpdateOperation,
Path: "/foo",
Connection: &logical.Connection{
RemoteAddr: "127.0.0.1",
},
WrapInfo: &logical.RequestWrapInfo{
TTL: 60 * time.Second,
},
Headers: map[string][]string{
"foo": []string{"bar"},
},
},
errors.New("this is an error"),
"@cee: ",
testFormatJSONReqBasicStr,
},
}
@ -44,7 +65,9 @@ func TestFormatJSON_formatRequest(t *testing.T) {
for name, tc := range cases {
var buf bytes.Buffer
formatter := AuditFormatter{
AuditFormatWriter: &JSONFormatWriter{},
AuditFormatWriter: &JSONFormatWriter{
Prefix: tc.Prefix,
},
}
salter, _ := salt.NewSalt(nil, nil)
config := FormatterConfig{
@ -54,13 +77,17 @@ func TestFormatJSON_formatRequest(t *testing.T) {
t.Fatalf("bad: %s\nerr: %s", name, err)
}
if !strings.HasPrefix(buf.String(), tc.Prefix) {
t.Fatalf("no prefix: %s \n log: %s\nprefix: %s", name, tc.Result, tc.Prefix)
}
var expectedjson = new(AuditRequestEntry)
if err := jsonutil.DecodeJSON([]byte(tc.Result), &expectedjson); err != nil {
t.Fatalf("bad json: %s", err)
}
var actualjson = new(AuditRequestEntry)
if err := jsonutil.DecodeJSON([]byte(buf.String()), &actualjson); err != nil {
if err := jsonutil.DecodeJSON([]byte(buf.String())[len(tc.Prefix):], &actualjson); err != nil {
t.Fatalf("bad json: %s", err)
}
@ -71,7 +98,7 @@ func TestFormatJSON_formatRequest(t *testing.T) {
t.Fatalf("unable to marshal json: %s", err)
}
if strings.TrimSpace(buf.String()) != string(expectedBytes) {
if !strings.HasSuffix(strings.TrimSpace(buf.String()), string(expectedBytes)) {
t.Fatalf(
"bad: %s\nResult:\n\n'%s'\n\nExpected:\n\n'%s'",
name, buf.String(), string(expectedBytes))

View file

@ -10,13 +10,22 @@ import (
// JSONxFormatWriter is an AuditFormatWriter implementation that structures data into
// a XML format.
type JSONxFormatWriter struct{}
type JSONxFormatWriter struct {
Prefix string
}
func (f *JSONxFormatWriter) WriteRequest(w io.Writer, req *AuditRequestEntry) error {
if req == nil {
return fmt.Errorf("request entry was nil, cannot encode")
}
if len(f.Prefix) > 0 {
_, err := w.Write([]byte(f.Prefix))
if err != nil {
return err
}
}
jsonBytes, err := json.Marshal(req)
if err != nil {
return err
@ -36,6 +45,13 @@ func (f *JSONxFormatWriter) WriteResponse(w io.Writer, resp *AuditResponseEntry)
return fmt.Errorf("response entry was nil, cannot encode")
}
if len(f.Prefix) > 0 {
_, err := w.Write([]byte(f.Prefix))
if err != nil {
return err
}
}
jsonBytes, err := json.Marshal(resp)
if err != nil {
return err

View file

@ -17,6 +17,7 @@ func TestFormatJSONx_formatRequest(t *testing.T) {
Auth *logical.Auth
Req *logical.Request
Err error
Prefix string
Result string
Expected string
}{
@ -37,6 +38,27 @@ func TestFormatJSONx_formatRequest(t *testing.T) {
},
errors.New("this is an error"),
"",
"",
`<json:object name="auth"><json:string name="accessor"></json:string><json:string name="client_token"></json:string><json:string name="display_name"></json:string><json:null name="metadata" /><json:array name="policies"><json:string>root</json:string></json:array></json:object><json:string name="error">this is an error</json:string><json:object name="request"><json:string name="client_token"></json:string><json:string name="client_token_accessor"></json:string><json:null name="data" /><json:object name="headers"><json:array name="foo"><json:string>bar</json:string></json:array></json:object><json:string name="id"></json:string><json:string name="operation">update</json:string><json:string name="path">/foo</json:string><json:string name="remote_address">127.0.0.1</json:string><json:number name="wrap_ttl">60</json:number></json:object><json:string name="type">request</json:string>`,
},
"auth, request with prefix": {
&logical.Auth{ClientToken: "foo", Policies: []string{"root"}},
&logical.Request{
Operation: logical.UpdateOperation,
Path: "/foo",
Connection: &logical.Connection{
RemoteAddr: "127.0.0.1",
},
WrapInfo: &logical.RequestWrapInfo{
TTL: 60 * time.Second,
},
Headers: map[string][]string{
"foo": []string{"bar"},
},
},
errors.New("this is an error"),
"",
"@cee: ",
`<json:object name="auth"><json:string name="accessor"></json:string><json:string name="client_token"></json:string><json:string name="display_name"></json:string><json:null name="metadata" /><json:array name="policies"><json:string>root</json:string></json:array></json:object><json:string name="error">this is an error</json:string><json:object name="request"><json:string name="client_token"></json:string><json:string name="client_token_accessor"></json:string><json:null name="data" /><json:object name="headers"><json:array name="foo"><json:string>bar</json:string></json:array></json:object><json:string name="id"></json:string><json:string name="operation">update</json:string><json:string name="path">/foo</json:string><json:string name="remote_address">127.0.0.1</json:string><json:number name="wrap_ttl">60</json:number></json:object><json:string name="type">request</json:string>`,
},
}
@ -44,7 +66,9 @@ func TestFormatJSONx_formatRequest(t *testing.T) {
for name, tc := range cases {
var buf bytes.Buffer
formatter := AuditFormatter{
AuditFormatWriter: &JSONxFormatWriter{},
AuditFormatWriter: &JSONxFormatWriter{
Prefix: tc.Prefix,
},
}
salter, _ := salt.NewSalt(nil, nil)
config := FormatterConfig{
@ -55,7 +79,11 @@ func TestFormatJSONx_formatRequest(t *testing.T) {
t.Fatalf("bad: %s\nerr: %s", name, err)
}
if strings.TrimSpace(buf.String()) != string(tc.Expected) {
if !strings.HasPrefix(buf.String(), tc.Prefix) {
t.Fatalf("no prefix: %s \n log: %s\nprefix: %s", name, tc.Result, tc.Prefix)
}
if !strings.HasSuffix(strings.TrimSpace(buf.String()), string(tc.Expected)) {
t.Fatalf(
"bad: %s\nResult:\n\n'%s'\n\nExpected:\n\n'%s'",
name, strings.TrimSpace(buf.String()), string(tc.Expected))

View file

@ -76,9 +76,13 @@ func Factory(conf *audit.BackendConfig) (audit.Backend, error) {
switch format {
case "json":
b.formatter.AuditFormatWriter = &audit.JSONFormatWriter{}
b.formatter.AuditFormatWriter = &audit.JSONFormatWriter{
Prefix: conf.Config["prefix"],
}
case "jsonx":
b.formatter.AuditFormatWriter = &audit.JSONxFormatWriter{}
b.formatter.AuditFormatWriter = &audit.JSONxFormatWriter{
Prefix: conf.Config["prefix"],
}
}
// Ensure that the file can be successfully opened for writing;

View file

@ -87,9 +87,13 @@ func Factory(conf *audit.BackendConfig) (audit.Backend, error) {
switch format {
case "json":
b.formatter.AuditFormatWriter = &audit.JSONFormatWriter{}
b.formatter.AuditFormatWriter = &audit.JSONFormatWriter{
Prefix: conf.Config["prefix"],
}
case "jsonx":
b.formatter.AuditFormatWriter = &audit.JSONxFormatWriter{}
b.formatter.AuditFormatWriter = &audit.JSONxFormatWriter{
Prefix: conf.Config["prefix"],
}
}
return b, nil

View file

@ -74,9 +74,13 @@ func Factory(conf *audit.BackendConfig) (audit.Backend, error) {
switch format {
case "json":
b.formatter.AuditFormatWriter = &audit.JSONFormatWriter{}
b.formatter.AuditFormatWriter = &audit.JSONFormatWriter{
Prefix: conf.Config["prefix"],
}
case "jsonx":
b.formatter.AuditFormatWriter = &audit.JSONxFormatWriter{}
b.formatter.AuditFormatWriter = &audit.JSONxFormatWriter{
Prefix: conf.Config["prefix"],
}
}
return b, nil

View file

@ -272,10 +272,10 @@ func (b *backend) pathRoleRead(
// HMAC key belonging to the role should NOT be exported.
delete(respData, "hmac_key")
// Display the ttl in seconds.
// Display all the durations in seconds
respData["ttl"] = roleEntry.TTL / time.Second
// Display the max_ttl in seconds.
respData["max_ttl"] = roleEntry.MaxTTL / time.Second
respData["period"] = roleEntry.Period / time.Second
return &logical.Response{
Data: respData,

View file

@ -0,0 +1,59 @@
package awsec2
import (
"testing"
"time"
"github.com/hashicorp/vault/logical"
)
func TestAwsEc2_RoleDurationSeconds(t *testing.T) {
config := logical.TestBackendConfig()
storage := &logical.InmemStorage{}
config.StorageView = storage
b, err := Backend(config)
if err != nil {
t.Fatal(err)
}
_, err = b.Setup(config)
if err != nil {
t.Fatal(err)
}
roleData := map[string]interface{}{
"bound_iam_instance_profile_arn": "testarn",
"ttl": "10s",
"max_ttl": "20s",
"period": "30s",
}
roleReq := &logical.Request{
Operation: logical.UpdateOperation,
Storage: storage,
Path: "role/testrole",
Data: roleData,
}
resp, err := b.HandleRequest(roleReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("resp: %#v, err: %v", resp, err)
}
roleReq.Operation = logical.ReadOperation
resp, err = b.HandleRequest(roleReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("resp: %#v, err: %v", resp, err)
}
if int64(resp.Data["ttl"].(time.Duration)) != 10 {
t.Fatalf("bad: period; expected: 10, actual: %d", resp.Data["ttl"])
}
if int64(resp.Data["max_ttl"].(time.Duration)) != 20 {
t.Fatalf("bad: period; expected: 20, actual: %d", resp.Data["max_ttl"])
}
if int64(resp.Data["period"].(time.Duration)) != 30 {
t.Fatalf("bad: period; expected: 30, actual: %d", resp.Data["period"])
}
}

View file

@ -135,8 +135,7 @@ func TestBackend_basic(t *testing.T) {
b := factory(t)
logicaltest.Test(t, logicaltest.TestCase{
AcceptanceTest: true,
Backend: b,
Backend: b,
Steps: []logicaltest.TestStep{
testAccStepConfigUrl(t),
// Map Scientists group (from LDAP server) with foo policy
@ -164,8 +163,7 @@ func TestBackend_basic_authbind(t *testing.T) {
b := factory(t)
logicaltest.Test(t, logicaltest.TestCase{
AcceptanceTest: true,
Backend: b,
Backend: b,
Steps: []logicaltest.TestStep{
testAccStepConfigUrlWithAuthBind(t),
testAccStepGroup(t, "Scientists", "foo"),
@ -180,8 +178,7 @@ func TestBackend_basic_discover(t *testing.T) {
b := factory(t)
logicaltest.Test(t, logicaltest.TestCase{
AcceptanceTest: true,
Backend: b,
Backend: b,
Steps: []logicaltest.TestStep{
testAccStepConfigUrlWithDiscover(t),
testAccStepGroup(t, "Scientists", "foo"),
@ -196,8 +193,7 @@ func TestBackend_basic_nogroupdn(t *testing.T) {
b := factory(t)
logicaltest.Test(t, logicaltest.TestCase{
AcceptanceTest: true,
Backend: b,
Backend: b,
Steps: []logicaltest.TestStep{
testAccStepConfigUrlNoGroupDN(t),
testAccStepGroup(t, "Scientists", "foo"),
@ -212,8 +208,7 @@ func TestBackend_groupCrud(t *testing.T) {
b := factory(t)
logicaltest.Test(t, logicaltest.TestCase{
AcceptanceTest: true,
Backend: b,
Backend: b,
Steps: []logicaltest.TestStep{
testAccStepGroup(t, "g1", "foo"),
testAccStepReadGroup(t, "g1", "default,foo"),
@ -230,8 +225,7 @@ func TestBackend_configDefaultsAfterUpdate(t *testing.T) {
b := factory(t)
logicaltest.Test(t, logicaltest.TestCase{
AcceptanceTest: false,
Backend: b,
Backend: b,
Steps: []logicaltest.TestStep{
logicaltest.TestStep{
Operation: logical.UpdateOperation,
@ -297,7 +291,8 @@ func testAccStepConfigUrlWithAuthBind(t *testing.T) logicaltest.TestStep {
Data: map[string]interface{}{
// Online LDAP test server
// http://www.forumsys.com/tutorials/integration-how-to/ldap/online-ldap-test-server/
"url": "ldap://ldap.forumsys.com",
// In this test we also exercise multiple URL support
"url": "foobar://ldap.example.com,ldap://ldap.forumsys.com",
"userattr": "uid",
"userdn": "dc=example,dc=com",
"groupdn": "dc=example,dc=com",
@ -388,8 +383,7 @@ func TestBackend_userCrud(t *testing.T) {
b := Backend()
logicaltest.Test(t, logicaltest.TestCase{
AcceptanceTest: true,
Backend: b,
Backend: b,
Steps: []logicaltest.TestStep{
testAccStepUser(t, "g1", "bar"),
testAccStepReadUser(t, "g1", "bar"),

View file

@ -11,9 +11,11 @@ import (
"github.com/fatih/structs"
"github.com/go-ldap/ldap"
multierror "github.com/hashicorp/go-multierror"
"github.com/hashicorp/vault/helper/tlsutil"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
log "github.com/mgutz/logxi/v1"
)
func pathConfig(b *backend) *framework.Path {
@ -23,7 +25,7 @@ func pathConfig(b *backend) *framework.Path {
"url": &framework.FieldSchema{
Type: framework.TypeString,
Default: "ldap://127.0.0.1",
Description: "ldap URL to connect to (default: ldap://127.0.0.1)",
Description: "LDAP URL to connect to (default: ldap://127.0.0.1). Multiple URLs can be specified by concatenating them with commas; they will be tried in-order.",
},
"userdn": &framework.FieldSchema{
@ -155,6 +157,8 @@ func (b *backend) Config(req *logical.Request) (*ConfigEntry, error) {
return nil, err
}
result.logger = b.Logger()
return result, nil
}
@ -183,6 +187,8 @@ func (b *backend) pathConfigRead(
func (b *backend) newConfigEntry(d *framework.FieldData) (*ConfigEntry, error) {
cfg := new(ConfigEntry)
cfg.logger = b.Logger()
url := d.Get("url").(string)
if url != "" {
cfg.Url = strings.ToLower(url)
@ -294,6 +300,7 @@ func (b *backend) pathConfigWrite(
}
type ConfigEntry struct {
logger log.Logger
Url string `json:"url" structs:"url" mapstructure:"url"`
UserDN string `json:"userdn" structs:"userdn" mapstructure:"userdn"`
GroupDN string `json:"groupdn" structs:"groupdn" mapstructure:"groupdn"`
@ -348,55 +355,67 @@ func (c *ConfigEntry) GetTLSConfig(host string) (*tls.Config, error) {
}
func (c *ConfigEntry) DialLDAP() (*ldap.Conn, error) {
u, err := url.Parse(c.Url)
if err != nil {
return nil, err
}
host, port, err := net.SplitHostPort(u.Host)
if err != nil {
host = u.Host
}
var retErr *multierror.Error
var conn *ldap.Conn
var tlsConfig *tls.Config
switch u.Scheme {
case "ldap":
if port == "" {
port = "389"
}
conn, err = ldap.Dial("tcp", host+":"+port)
urls := strings.Split(c.Url, ",")
for _, uut := range urls {
u, err := url.Parse(uut)
if err != nil {
break
retErr = multierror.Append(retErr, fmt.Errorf("error parsing url %q: %s", uut, err.Error()))
continue
}
if conn == nil {
err = fmt.Errorf("empty connection after dialing")
break
host, port, err := net.SplitHostPort(u.Host)
if err != nil {
host = u.Host
}
if c.StartTLS {
var tlsConfig *tls.Config
switch u.Scheme {
case "ldap":
if port == "" {
port = "389"
}
conn, err = ldap.Dial("tcp", net.JoinHostPort(host, port))
if err != nil {
break
}
if conn == nil {
err = fmt.Errorf("empty connection after dialing")
break
}
if c.StartTLS {
tlsConfig, err = c.GetTLSConfig(host)
if err != nil {
break
}
err = conn.StartTLS(tlsConfig)
}
case "ldaps":
if port == "" {
port = "636"
}
tlsConfig, err = c.GetTLSConfig(host)
if err != nil {
break
}
err = conn.StartTLS(tlsConfig)
conn, err = ldap.DialTLS("tcp", net.JoinHostPort(host, port), tlsConfig)
default:
retErr = multierror.Append(retErr, fmt.Errorf("invalid LDAP scheme in url %q"))
continue
}
case "ldaps":
if port == "" {
port = "636"
}
tlsConfig, err = c.GetTLSConfig(host)
if err != nil {
if err == nil {
if retErr != nil {
if c.logger.IsDebug() {
c.logger.Debug("ldap: errors connecting to some hosts: %s", retErr.Error())
}
}
retErr = nil
break
}
conn, err = ldap.DialTLS("tcp", host+":"+port, tlsConfig)
default:
return nil, fmt.Errorf("invalid LDAP scheme")
}
if err != nil {
return nil, fmt.Errorf("cannot connect to LDAP: %v", err)
retErr = multierror.Append(retErr, fmt.Errorf("error connecting to host %q: %s", uut, err.Error()))
}
return conn, nil
return conn, retErr.ErrorOrNil()
}
/*

View file

@ -59,10 +59,6 @@ func (b *backend) Login(req *logical.Request, username string, password string)
return nil, logical.ErrorResponse("okta auth backend unexpected failure"), nil
}
if b.Logger().IsDebug() {
b.Logger().Debug("auth/okta:", auth)
}
oktaGroups, err := b.getOktaGroups(cfg, auth.Embedded.User.ID)
if err != nil {
return nil, logical.ErrorResponse(err.Error()), nil
@ -96,13 +92,15 @@ func (b *backend) Login(req *logical.Request, username string, password string)
var policies []string
for _, groupName := range allGroups {
group, err := b.Group(req.Storage, groupName)
if err == nil && group != nil {
if err == nil && group != nil && group.Policies != nil {
policies = append(policies, group.Policies...)
}
}
// Merge local Policies into Okta Policies
policies = append(policies, user.Policies...)
if user != nil && user.Policies != nil {
policies = append(policies, user.Policies...)
}
if len(policies) == 0 {
errStr := "user is not a member of any authorized policy"

View file

@ -7,6 +7,7 @@ import (
"testing"
"github.com/hashicorp/vault/helper/logformat"
"github.com/hashicorp/vault/helper/policyutil"
log "github.com/mgutz/logxi/v1"
"github.com/hashicorp/vault/logical"
@ -40,23 +41,23 @@ func TestBackend_Config(t *testing.T) {
Backend: b,
Steps: []logicaltest.TestStep{
testConfigCreate(t, configData),
testLoginWrite(t, username, "wrong", "E0000004", 0),
testLoginWrite(t, username, password, "user is not a member of any authorized policy", 0),
testLoginWrite(t, username, "wrong", "E0000004", nil),
testLoginWrite(t, username, password, "user is not a member of any authorized policy", nil),
testAccUserGroups(t, username, "local_group,local_group2"),
testAccGroups(t, "local_group", "local_group_policy"),
testLoginWrite(t, username, password, "", 2),
testLoginWrite(t, username, password, "", []string{"local_group_policy"}),
testAccGroups(t, "Everyone", "everyone_group_policy,every_group_policy2"),
testLoginWrite(t, username, password, "", 2),
testLoginWrite(t, username, password, "", []string{"local_group_policy"}),
testConfigUpdate(t, configDataToken),
testConfigRead(t, configData),
testLoginWrite(t, username, password, "", 4),
testAccGroups(t, "TestGroup", "testgroup_group_policy"),
testLoginWrite(t, username, password, "", 5),
testLoginWrite(t, username, password, "", []string{"everyone_group_policy", "every_group_policy2", "local_group_policy"}),
testAccGroups(t, "local_group2", "testgroup_group_policy"),
testLoginWrite(t, username, password, "", []string{"everyone_group_policy", "every_group_policy2", "local_group_policy", "testgroup_group_policy"}),
},
})
}
func testLoginWrite(t *testing.T, username, password, reason string, policies int) logicaltest.TestStep {
func testLoginWrite(t *testing.T, username, password, reason string, policies []string) logicaltest.TestStep {
return logicaltest.TestStep{
Operation: logical.UpdateOperation,
Path: "login/" + username,
@ -72,8 +73,8 @@ func testLoginWrite(t *testing.T, username, password, reason string, policies in
}
if resp.Auth != nil {
if len(resp.Auth.Policies) != policies {
return fmt.Errorf("policy mismatch expected %d but got %s", policies, resp.Auth.Policies)
if !policyutil.EquivalentPolicies(resp.Auth.Policies, policies) {
return fmt.Errorf("policy mismatch expected %v but got %v", policies, resp.Auth.Policies)
}
}

View file

@ -312,15 +312,19 @@ func (c *ServerCommand) Run(args []string) int {
return 1
}
host, port, err := net.SplitHostPort(u.Host)
nPort, nPortErr := strconv.Atoi(port)
if err != nil {
// assume it's due to there not being a port specified, in which case
// use 443
host = u.Host
nPort = 443
// This sucks, as it's a const in the function but not exported in the package
if strings.Contains(err.Error(), "missing port in address") {
host = u.Host
port = "443"
} else {
c.Ui.Output(fmt.Sprintf("Error parsing redirect address: %v", err))
return 1
}
}
if nPortErr != nil {
c.Ui.Output(fmt.Sprintf("Cannot parse %s as a numeric port: %v", port, nPortErr))
nPort, err := strconv.Atoi(port)
if err != nil {
c.Ui.Output(fmt.Sprintf("Error parsing redirect address; failed to convert %q to a numeric: %v", port, err))
return 1
}
u.Host = net.JoinHostPort(host, strconv.Itoa(nPort+1))

View file

@ -2,11 +2,15 @@ package vault
import (
"fmt"
"strings"
"sync"
"github.com/hashicorp/vault/logical"
)
// N.B.: While we could use textproto to get the canonical mime header, HTTP/2
// requires all headers to be converted to lower case, so we just do that.
const (
// Key used in the BarrierView to store and retrieve the header config
auditedHeadersEntry = "audited-headers"
@ -37,7 +41,7 @@ func (a *AuditedHeadersConfig) add(header string, hmac bool) error {
a.Lock()
defer a.Unlock()
a.Headers[header] = &auditedHeaderSettings{hmac}
a.Headers[strings.ToLower(header)] = &auditedHeaderSettings{hmac}
entry, err := logical.StorageEntryJSON(auditedHeadersEntry, a.Headers)
if err != nil {
return fmt.Errorf("failed to persist audited headers config: %v", err)
@ -60,7 +64,7 @@ func (a *AuditedHeadersConfig) remove(header string) error {
a.Lock()
defer a.Unlock()
delete(a.Headers, header)
delete(a.Headers, strings.ToLower(header))
entry, err := logical.StorageEntryJSON(auditedHeadersEntry, a.Headers)
if err != nil {
return fmt.Errorf("failed to persist audited headers config: %v", err)
@ -80,9 +84,16 @@ func (a *AuditedHeadersConfig) ApplyConfig(headers map[string][]string, hashFunc
a.RLock()
defer a.RUnlock()
// Make a copy of the incoming headers with everything lower so we can
// case-insensitively compare
lowerHeaders := make(map[string][]string, len(headers))
for k, v := range headers {
lowerHeaders[strings.ToLower(k)] = v
}
result = make(map[string][]string, len(a.Headers))
for key, settings := range a.Headers {
if val, ok := headers[key]; ok {
if val, ok := lowerHeaders[key]; ok {
// copy the header values so we don't overwrite them
hVals := make([]string, len(val))
copy(hVals, val)
@ -120,8 +131,15 @@ func (c *Core) setupAuditedHeadersConfig() error {
}
}
// Ensure that we are able to case-sensitively access the headers;
// necessary for the upgrade case
lowerHeaders := make(map[string]*auditedHeaderSettings, len(headers))
for k, v := range headers {
lowerHeaders[strings.ToLower(k)] = v
}
c.auditedHeaders = &AuditedHeadersConfig{
Headers: headers,
Headers: lowerHeaders,
view: view,
}

View file

@ -29,7 +29,7 @@ func testAuditedHeadersConfig_Add(t *testing.T, conf *AuditedHeadersConfig) {
t.Fatalf("Error when adding header to config: %s", err)
}
settings, ok := conf.Headers["X-Test-Header"]
settings, ok := conf.Headers["x-test-header"]
if !ok {
t.Fatal("Expected header to be found in config")
}
@ -50,7 +50,7 @@ func testAuditedHeadersConfig_Add(t *testing.T, conf *AuditedHeadersConfig) {
}
expected := map[string]*auditedHeaderSettings{
"X-Test-Header": &auditedHeaderSettings{
"x-test-header": &auditedHeaderSettings{
HMAC: false,
},
}
@ -64,7 +64,7 @@ func testAuditedHeadersConfig_Add(t *testing.T, conf *AuditedHeadersConfig) {
t.Fatalf("Error when adding header to config: %s", err)
}
settings, ok = conf.Headers["X-Vault-Header"]
settings, ok = conf.Headers["x-vault-header"]
if !ok {
t.Fatal("Expected header to be found in config")
}
@ -84,7 +84,7 @@ func testAuditedHeadersConfig_Add(t *testing.T, conf *AuditedHeadersConfig) {
t.Fatalf("Error decoding header view: %s", err)
}
expected["X-Vault-Header"] = &auditedHeaderSettings{
expected["x-vault-header"] = &auditedHeaderSettings{
HMAC: true,
}
@ -100,7 +100,7 @@ func testAuditedHeadersConfig_Remove(t *testing.T, conf *AuditedHeadersConfig) {
t.Fatalf("Error when adding header to config: %s", err)
}
_, ok := conf.Headers["X-Test-Header"]
_, ok := conf.Headers["x-Test-HeAder"]
if ok {
t.Fatal("Expected header to not be found in config")
}
@ -117,7 +117,7 @@ func testAuditedHeadersConfig_Remove(t *testing.T, conf *AuditedHeadersConfig) {
}
expected := map[string]*auditedHeaderSettings{
"X-Vault-Header": &auditedHeaderSettings{
"x-vault-header": &auditedHeaderSettings{
HMAC: true,
},
}
@ -126,12 +126,12 @@ func testAuditedHeadersConfig_Remove(t *testing.T, conf *AuditedHeadersConfig) {
t.Fatalf("Expected config didn't match actual. Expected: %#v, Got: %#v", expected, headers)
}
err = conf.remove("X-Vault-Header")
err = conf.remove("x-VaulT-Header")
if err != nil {
t.Fatalf("Error when adding header to config: %s", err)
}
_, ok = conf.Headers["X-Vault-Header"]
_, ok = conf.Headers["x-vault-header"]
if ok {
t.Fatal("Expected header to not be found in config")
}
@ -157,10 +157,8 @@ func testAuditedHeadersConfig_Remove(t *testing.T, conf *AuditedHeadersConfig) {
func TestAuditedHeadersConfig_ApplyConfig(t *testing.T) {
conf := mockAuditedHeadersConfig(t)
conf.Headers = map[string]*auditedHeaderSettings{
"X-Test-Header": &auditedHeaderSettings{false},
"X-Vault-Header": &auditedHeaderSettings{true},
}
conf.add("X-TesT-Header", false)
conf.add("X-Vault-HeAdEr", true)
reqHeaders := map[string][]string{
"X-Test-Header": []string{"foo"},
@ -173,8 +171,8 @@ func TestAuditedHeadersConfig_ApplyConfig(t *testing.T) {
result := conf.ApplyConfig(reqHeaders, hashFunc)
expected := map[string][]string{
"X-Test-Header": []string{"foo"},
"X-Vault-Header": []string{"hashed", "hashed"},
"x-test-header": []string{"foo"},
"x-vault-header": []string{"hashed", "hashed"},
}
if !reflect.DeepEqual(result, expected) {

View file

@ -3,6 +3,7 @@ package cleanhttp
import (
"net"
"net/http"
"runtime"
"time"
)
@ -22,13 +23,15 @@ func DefaultTransport() *http.Transport {
func DefaultPooledTransport() *http.Transport {
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
Dial: (&net.Dialer{
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
TLSHandshakeTimeout: 10 * time.Second,
DisableKeepAlives: false,
MaxIdleConnsPerHost: 1,
}).DialContext,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
MaxIdleConnsPerHost: runtime.GOMAXPROCS(0) + 1,
}
return transport
}

6
vendor/vendor.json vendored
View file

@ -601,10 +601,10 @@
"revisionTime": "2014-10-28T05:47:10Z"
},
{
"checksumSHA1": "Uzyon2091lmwacNsl1hCytjhHtg=",
"checksumSHA1": "6ihdHMkDfFx/rJ1A36com2F6bQk=",
"path": "github.com/hashicorp/go-cleanhttp",
"revision": "ad28ea4487f05916463e2423a55166280e8254b5",
"revisionTime": "2016-04-07T17:41:26Z"
"revision": "a45970658e51fea2c41445ff0f7e07106d007617",
"revisionTime": "2017-02-11T00:33:01Z"
},
{
"checksumSHA1": "TNlVzNR1OaajcNi3CbQ3bGbaLGU=",

View file

@ -58,7 +58,7 @@ if [ -z "$NO_UPLOAD" ]; then
--no-mime-magic \
--acl-public \
--recursive \
--add-header="Cache-Control: max-age=31536000" \
--add-header="Cache-Control: max-age=14400" \
--add-header="x-amz-meta-surrogate-key: site-$PROJECT" \
sync "$DIR/build/" "s3://hc-sites/$PROJECT/latest/"
@ -67,6 +67,7 @@ if [ -z "$NO_UPLOAD" ]; then
echo "Overriding javascript mime-types..."
s3cmd \
--mime-type="application/javascript" \
--add-header="Cache-Control: max-age=31536000" \
--exclude "*" \
--include "*.js" \
--recursive \
@ -75,6 +76,7 @@ if [ -z "$NO_UPLOAD" ]; then
echo "Overriding css mime-types..."
s3cmd \
--mime-type="text/css" \
--add-header="Cache-Control: max-age=31536000" \
--exclude "*" \
--include "*.css" \
--recursive \
@ -83,11 +85,11 @@ if [ -z "$NO_UPLOAD" ]; then
echo "Overriding svg mime-types..."
s3cmd \
--mime-type="image/svg+xml" \
--add-header="Cache-Control: max-age=31536000" \
--exclude "*" \
--include "*.svg" \
--recursive \
modify "s3://hc-sites/$PROJECT/latest/"
fi
# Perform a soft-purge of the surrogate key.

View file

@ -85,6 +85,12 @@ Following are the configuration options available for the backend.
Allows selecting the output format. Valid values are `json` (the
default) and `jsonx`, which formats the normal log entries as XML.
</li>
<li>
<span class="param">prefix</span>
<span class="param-flags">optional</span>
Allows a customizable string prefix to write before the actual log
line. Defaults to an empty string.
</li>
</ul>
</dd>
</dl>

View file

@ -74,6 +74,12 @@ Following are the configuration options available for the backend.
<span class="param-flags">optional</span>
Sets the timeout for writes to the socket. Defaults to "2s" (2 seconds).
</li>
<li>
<span class="param">prefix</span>
<span class="param-flags">optional</span>
Allows a customizable string prefix to write before the actual log
line. Defaults to an empty string.
</li>
</ul>
</dd>
</dl>

View file

@ -71,6 +71,12 @@ Following are the configuration options available for the backend.
Allows selecting the output format. Valid values are `json` (the
default) and `jsonx`, which formats the normal log entries as XML.
</li>
<li>
<span class="param">prefix</span>
<span class="param-flags">optional</span>
Allows a customizable string prefix to write before the actual log
line. Defaults to an empty string.
</li>
</ul>
</dd>
</dl>

View file

@ -24,11 +24,11 @@ familiar with instance metadata, details can be found
One piece of "dynamic metadata" available to the EC2 instance, is the instance
identity document, a JSON representation of a collection of instance metadata.
Importantly, AWS also provides a copy of this metadata in PKCS#7 format signed
with its public key, and publishes the public keys used (which are grouped by
region). (Details on the instance identity document and the signature can be
AWS also provides PKCS#7 signature of the instance metadata document, and
publishes the public keys (grouped by region) which can be used to verify the
signature. Details on the instance identity document and the signature can be
found
[here](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html).)
[here](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html).
During login, the backend verifies the signature on the PKCS#7 document,
ensuring that the information contained within, is certified accurate by AWS.

View file

@ -111,7 +111,7 @@ Configuration is written to `auth/ldap/config`.
### Connection parameters
* `url` (string, required) - The LDAP server to connect to. Examples: `ldap://ldap.myorg.com`, `ldaps://ldap.myorg.com:636`
* `url` (string, required) - The LDAP server to connect to. Examples: `ldap://ldap.myorg.com`, `ldaps://ldap.myorg.com:636`. This can also be a comma-delineated list of URLs, e.g. `ldap://ldap.myorg.com,ldaps://ldap.myorg.com:636`, in which case the servers will be tried in-order if there are errors during the connection process.
* `starttls` (bool, optional) - If true, issues a `StartTLS` command after establishing an unencrypted connection.
* `insecure_tls` - (bool, optional) - If true, skips LDAP server SSL certificate verification - insecure, use with caution!
* `certificate` - (string, optional) - CA certificate to use when verifying LDAP server certificate, must be x509 PEM encoded.

View file

@ -64,8 +64,8 @@ sending a SIGHUP to the server process. These are denoted below.
subsystem. This will very significantly impact performance.
* `disable_mlock` (optional) - A boolean. If true, this will disable the
server from executing the `mlock` syscall to prevent memory from being
swapped to disk. This is not recommended in production (see below).
server from executing the `mlock` syscall. `mlock` prevents memory from being
swapped to disk. Disabling `mlock` is not recommended in production (see below).
* `telemetry` (optional) - Configures the telemetry reporting system
(see below).

View file

@ -79,6 +79,8 @@ These libraries are provided by the community.
* [vault-php-sdk](https://github.com/jippi/vault-php-sdk)
* `composer require jippi/vault-php-sdk`
* [vault-php-sdk](https://github.com/violuke/vault-php-sdk) extended from jipppi
* `composer require violuke/vault-php-sdk`
### Python

View file

@ -338,7 +338,7 @@ only encrypt or decrypt using the named keys they need access to.
Defaults to 0.
</li>
<li>
<span class="param">allow_deletion</span>
<span class="param">deletion_allowed</span>
<span class="param-flags">optional</span>
When set, the key is allowed to be deleted. Defaults to false.
</li>

View file

@ -79,7 +79,7 @@ remains mounted.
```
$ vault unmount generic/
Successfully unmounted 'generic/'!
Successfully unmounted 'generic/' if it was mounted
```
In addition to unmounting, you can remount a backend. Remounting a