Merge branch 'oss' into sys-tidy-leases

This commit is contained in:
vishalnayak 2017-05-01 09:58:58 -04:00
commit 60add30b9d
20 changed files with 372 additions and 92 deletions

View File

@ -21,6 +21,8 @@ FEATURES:
IMPROVEMENTS:
* auth/cert: Support for constraints on subject common name, DNS names and
Email addresses in the certificate [GH-2595]
* auth/ldap: Use the binding credentials to search group membership rather
than the user credentials [GH-2534]
* cli/revoke: Add `-self` option to allow revoking the currently active token

View File

@ -348,9 +348,9 @@ func TestBackend_CertWrites(t *testing.T) {
tc := logicaltest.TestCase{
Backend: testFactory(t),
Steps: []logicaltest.TestStep{
testAccStepCert(t, "aaa", ca1, "foo", false),
testAccStepCert(t, "bbb", ca2, "foo", false),
testAccStepCert(t, "ccc", ca3, "foo", true),
testAccStepCert(t, "aaa", ca1, "foo", "", false),
testAccStepCert(t, "bbb", ca2, "foo", "", false),
testAccStepCert(t, "ccc", ca3, "foo", "", true),
},
}
tc.Steps = append(tc.Steps, testAccStepListCerts(t, []string{"aaa", "bbb"})...)
@ -368,13 +368,17 @@ func TestBackend_basic_CA(t *testing.T) {
logicaltest.Test(t, logicaltest.TestCase{
Backend: testFactory(t),
Steps: []logicaltest.TestStep{
testAccStepCert(t, "web", ca, "foo", false),
testAccStepCert(t, "web", ca, "foo", "", false),
testAccStepLogin(t, connState),
testAccStepCertLease(t, "web", ca, "foo"),
testAccStepCertTTL(t, "web", ca, "foo"),
testAccStepLogin(t, connState),
testAccStepCertNoLease(t, "web", ca, "foo"),
testAccStepLoginDefaultLease(t, connState),
testAccStepCert(t, "web", ca, "foo", "*.example.com", false),
testAccStepLogin(t, connState),
testAccStepCert(t, "web", ca, "foo", "*.invalid.com", false),
testAccStepLoginInvalid(t, connState),
},
})
}
@ -405,8 +409,29 @@ func TestBackend_Basic_CRLs(t *testing.T) {
})
}
// Test a self-signed client that is trusted
// Test a self-signed client (root CA) that is trusted
func TestBackend_basic_singleCert(t *testing.T) {
connState := testConnState(t, "test-fixtures/root/rootcacert.pem",
"test-fixtures/root/rootcakey.pem", "test-fixtures/root/rootcacert.pem")
ca, err := ioutil.ReadFile("test-fixtures/root/rootcacert.pem")
if err != nil {
t.Fatalf("err: %v", err)
}
logicaltest.Test(t, logicaltest.TestCase{
Backend: testFactory(t),
Steps: []logicaltest.TestStep{
testAccStepCert(t, "web", ca, "foo", "", false),
testAccStepLogin(t, connState),
testAccStepCert(t, "web", ca, "foo", "example.com", false),
testAccStepLogin(t, connState),
testAccStepCert(t, "web", ca, "foo", "invalid", false),
testAccStepLoginInvalid(t, connState),
},
})
}
// Test against a collection of matching and non-matching rules
func TestBackend_mixed_constraints(t *testing.T) {
connState := testConnState(t, "test-fixtures/keys/cert.pem",
"test-fixtures/keys/key.pem", "test-fixtures/root/rootcacert.pem")
ca, err := ioutil.ReadFile("test-fixtures/root/rootcacert.pem")
@ -416,13 +441,18 @@ func TestBackend_basic_singleCert(t *testing.T) {
logicaltest.Test(t, logicaltest.TestCase{
Backend: testFactory(t),
Steps: []logicaltest.TestStep{
testAccStepCert(t, "web", ca, "foo", false),
testAccStepCert(t, "1unconstrained", ca, "foo", "", false),
testAccStepCert(t, "2matching", ca, "foo", "*.example.com,whatever", false),
testAccStepCert(t, "3invalid", ca, "foo", "invalid", false),
testAccStepLogin(t, connState),
// Assumes CertEntries are processed in alphabetical order (due to store.List), so we only match 2matching if 1unconstrained doesn't match
testAccStepLoginWithName(t, connState, "2matching"),
testAccStepLoginWithNameInvalid(t, connState, "3invalid"),
},
})
}
// Test an untrusted self-signed client
// Test an untrusted client
func TestBackend_untrusted(t *testing.T) {
connState := testConnState(t, "test-fixtures/keys/cert.pem",
"test-fixtures/keys/key.pem", "test-fixtures/root/rootcacert.pem")
@ -476,6 +506,10 @@ func testAccStepDeleteCRL(t *testing.T, connState tls.ConnectionState) logicalte
}
func testAccStepLogin(t *testing.T, connState tls.ConnectionState) logicaltest.TestStep {
return testAccStepLoginWithName(t, connState, "")
}
func testAccStepLoginWithName(t *testing.T, connState tls.ConnectionState, certName string) logicaltest.TestStep {
return logicaltest.TestStep{
Operation: logical.UpdateOperation,
Path: "login",
@ -486,9 +520,16 @@ func testAccStepLogin(t *testing.T, connState tls.ConnectionState) logicaltest.T
t.Fatalf("bad lease length: %#v", resp.Auth)
}
if certName != "" && resp.Auth.DisplayName != ("mnt-"+certName) {
t.Fatalf("matched the wrong cert: %#v", resp.Auth.DisplayName)
}
fn := logicaltest.TestCheckAuth([]string{"default", "foo"})
return fn(resp)
},
Data: map[string]interface{}{
"name": certName,
},
}
}
@ -510,6 +551,10 @@ func testAccStepLoginDefaultLease(t *testing.T, connState tls.ConnectionState) l
}
func testAccStepLoginInvalid(t *testing.T, connState tls.ConnectionState) logicaltest.TestStep {
return testAccStepLoginWithNameInvalid(t, connState, "")
}
func testAccStepLoginWithNameInvalid(t *testing.T, connState tls.ConnectionState, certName string) logicaltest.TestStep {
return logicaltest.TestStep{
Operation: logical.UpdateOperation,
Path: "login",
@ -521,6 +566,9 @@ func testAccStepLoginInvalid(t *testing.T, connState tls.ConnectionState) logica
}
return nil
},
Data: map[string]interface{}{
"name": certName,
},
ErrorOk: true,
}
}
@ -572,16 +620,17 @@ func testAccStepListCerts(
}
func testAccStepCert(
t *testing.T, name string, cert []byte, policies string, expectError bool) logicaltest.TestStep {
t *testing.T, name string, cert []byte, policies string, allowedNames string, expectError bool) logicaltest.TestStep {
return logicaltest.TestStep{
Operation: logical.UpdateOperation,
Path: "certs/" + name,
ErrorOk: expectError,
Data: map[string]interface{}{
"certificate": string(cert),
"policies": policies,
"display_name": name,
"lease": 1000,
"certificate": string(cert),
"policies": policies,
"display_name": name,
"allowed_names": allowedNames,
"lease": 1000,
},
Check: func(resp *logical.Response) error {
if resp == nil && expectError {
@ -730,10 +779,17 @@ func Test_Renew(t *testing.T) {
t.Fatal(err)
}
resp, err = b.pathLogin(req, nil)
empty_login_fd := &framework.FieldData{
Raw: map[string]interface{}{},
Schema: pathLogin(b).Fields,
}
resp, err = b.pathLogin(req, empty_login_fd)
if err != nil {
t.Fatal(err)
}
if resp.IsError() {
t.Fatalf("got error: %#v", *resp)
}
req.Auth.InternalData = resp.Auth.InternalData
req.Auth.Metadata = resp.Auth.Metadata
req.Auth.LeaseOptions = resp.Auth.LeaseOptions
@ -741,7 +797,7 @@ func Test_Renew(t *testing.T) {
req.Auth.IssueTime = time.Now()
// Normal renewal
resp, err = b.pathLoginRenew(req, nil)
resp, err = b.pathLoginRenew(req, empty_login_fd)
if err != nil {
t.Fatal(err)
}
@ -759,7 +815,7 @@ func Test_Renew(t *testing.T) {
t.Fatal(err)
}
resp, err = b.pathLoginRenew(req, nil)
resp, err = b.pathLoginRenew(req, empty_login_fd)
if err == nil {
t.Fatal("expected error")
}
@ -771,7 +827,7 @@ func Test_Renew(t *testing.T) {
t.Fatal(err)
}
resp, err = b.pathLoginRenew(req, nil)
resp, err = b.pathLoginRenew(req, empty_login_fd)
if err != nil {
t.Fatal(err)
}
@ -788,7 +844,7 @@ func Test_Renew(t *testing.T) {
t.Fatal(err)
}
resp, err = b.pathLoginRenew(req, nil)
resp, err = b.pathLoginRenew(req, empty_login_fd)
if err != nil {
t.Fatal(err)
}

View File

@ -13,6 +13,7 @@ type CLIHandler struct{}
func (h *CLIHandler) Auth(c *api.Client, m map[string]string) (string, error) {
var data struct {
Mount string `mapstructure:"mount"`
Name string `mapstructure:"name"`
}
if err := mapstructure.WeakDecode(m, &data); err != nil {
return "", err
@ -22,8 +23,11 @@ func (h *CLIHandler) Auth(c *api.Client, m map[string]string) (string, error) {
data.Mount = "cert"
}
options := map[string]interface{}{
"name": data.Name,
}
path := fmt.Sprintf("auth/%s/login", data.Mount)
secret, err := c.Logical().Write(path, nil)
secret, err := c.Logical().Write(path, options)
if err != nil {
return "", err
}
@ -38,10 +42,13 @@ func (h *CLIHandler) Help() string {
help := `
The "cert" credential provider allows you to authenticate with a
client certificate. No other authentication materials are needed.
Optionally, you may specify the specific certificate role to
authenticate against with the "name" parameter.
Example: vault auth -method=cert \
-client-cert=/path/to/cert.pem \
-client-key=/path/to/key.pem
name=cert1
`

View File

@ -39,6 +39,12 @@ func pathCerts(b *backend) *framework.Path {
Must be x509 PEM encoded.`,
},
"allowed_names": &framework.FieldSchema{
Type: framework.TypeCommaStringSlice,
Description: `A comma-separated list of names.
At least one must exist in either the Common Name or SANs. Supports globbing.`,
},
"display_name": &framework.FieldSchema{
Type: framework.TypeString,
Description: `The display name to use for clients using this
@ -139,6 +145,7 @@ func (b *backend) pathCertWrite(
certificate := d.Get("certificate").(string)
displayName := d.Get("display_name").(string)
policies := policyutil.ParsePolicies(d.Get("policies").(string))
allowedNames := d.Get("allowed_names").([]string)
// Default the display name to the certificate name if not given
if displayName == "" {
@ -165,10 +172,11 @@ func (b *backend) pathCertWrite(
}
certEntry := &CertEntry{
Name: name,
Certificate: certificate,
DisplayName: displayName,
Policies: policies,
Name: name,
Certificate: certificate,
DisplayName: displayName,
Policies: policies,
AllowedNames: allowedNames,
}
// Parse the lease duration or default to backend/system default
@ -196,11 +204,12 @@ func (b *backend) pathCertWrite(
}
type CertEntry struct {
Name string
Certificate string
DisplayName string
Policies []string
TTL time.Duration
Name string
Certificate string
DisplayName string
Policies []string
TTL time.Duration
AllowedNames []string
}
const pathCertHelpSyn = `

View File

@ -14,6 +14,8 @@ import (
"github.com/hashicorp/vault/helper/policyutil"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
"github.com/ryanuber/go-glob"
)
// ParsedCert is a certificate that has been configured as trusted
@ -25,7 +27,12 @@ type ParsedCert struct {
func pathLogin(b *backend) *framework.Path {
return &framework.Path{
Pattern: "login",
Fields: map[string]*framework.FieldSchema{},
Fields: map[string]*framework.FieldSchema{
"name": &framework.FieldSchema{
Type: framework.TypeString,
Description: "The name of the certificate role to authenticate against.",
},
},
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.UpdateOperation: b.pathLogin,
},
@ -36,7 +43,7 @@ func (b *backend) pathLogin(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
var matched *ParsedCert
if verifyResp, resp, err := b.verifyCredentials(req); err != nil {
if verifyResp, resp, err := b.verifyCredentials(req, data); err != nil {
return nil, err
} else if resp != nil {
return resp, nil
@ -93,7 +100,7 @@ func (b *backend) pathLoginRenew(
if !config.DisableBinding {
var matched *ParsedCert
if verifyResp, resp, err := b.verifyCredentials(req); err != nil {
if verifyResp, resp, err := b.verifyCredentials(req, d); err != nil {
return nil, err
} else if resp != nil {
return resp, nil
@ -136,7 +143,7 @@ func (b *backend) pathLoginRenew(
return framework.LeaseExtend(cert.TTL, 0, b.System())(req, d)
}
func (b *backend) verifyCredentials(req *logical.Request) (*ParsedCert, *logical.Response, error) {
func (b *backend) verifyCredentials(req *logical.Request, d *framework.FieldData) (*ParsedCert, *logical.Response, error) {
// Get the connection state
if req.Connection == nil || req.Connection.ConnState == nil {
return nil, logical.ErrorResponse("tls connection required"), nil
@ -146,20 +153,29 @@ func (b *backend) verifyCredentials(req *logical.Request) (*ParsedCert, *logical
if connState.PeerCertificates == nil || len(connState.PeerCertificates) == 0 {
return nil, logical.ErrorResponse("client certificate must be supplied"), nil
}
clientCert := connState.PeerCertificates[0]
// Allow constraining the login request to a single CertEntry
certName := d.Get("name").(string)
// Load the trusted certificates
roots, trusted, trustedNonCAs := b.loadTrustedCerts(req.Storage)
roots, trusted, trustedNonCAs := b.loadTrustedCerts(req.Storage, certName)
// If trustedNonCAs is not empty it means that client had registered a non-CA cert
// with the backend.
if len(trustedNonCAs) != 0 {
policy := b.matchNonCAPolicy(connState.PeerCertificates[0], trustedNonCAs)
if policy != nil && !b.checkForChainInCRLs(policy.Certificates) {
return policy, nil, nil
for _, trustedNonCA := range trustedNonCAs {
tCert := trustedNonCA.Certificates[0]
// Check for client cert being explicitly listed in the config (and matching other constraints)
if tCert.SerialNumber.Cmp(clientCert.SerialNumber) == 0 &&
bytes.Equal(tCert.AuthorityKeyId, clientCert.AuthorityKeyId) &&
b.matchesConstraints(clientCert, trustedNonCA.Certificates, trustedNonCA) {
return trustedNonCA, nil, nil
}
}
}
// Validate the connection state is trusted
// Get the list of full chains matching the connection
trustedChains, err := validateConnState(roots, connState)
if err != nil {
return nil, nil, err
@ -170,49 +186,58 @@ func (b *backend) verifyCredentials(req *logical.Request) (*ParsedCert, *logical
return nil, logical.ErrorResponse("invalid certificate or no client certificate supplied"), nil
}
validChain := b.checkForValidChain(trustedChains)
if !validChain {
return nil, logical.ErrorResponse(
"no chain containing non-revoked certificates could be found for this login certificate",
), nil
}
// Match the trusted chain with the policy
return b.matchPolicy(trustedChains, trusted), nil, nil
}
// matchNonCAPolicy is used to match the client cert with the registered non-CA
// policies to establish client identity.
func (b *backend) matchNonCAPolicy(clientCert *x509.Certificate, trustedNonCAs []*ParsedCert) *ParsedCert {
for _, trustedNonCA := range trustedNonCAs {
tCert := trustedNonCA.Certificates[0]
if tCert.SerialNumber.Cmp(clientCert.SerialNumber) == 0 && bytes.Equal(tCert.AuthorityKeyId, clientCert.AuthorityKeyId) {
return trustedNonCA
}
}
return nil
}
// matchPolicy is used to match the associated policy with the certificate that
// was used to establish the client identity.
func (b *backend) matchPolicy(chains [][]*x509.Certificate, trusted []*ParsedCert) *ParsedCert {
// There is probably a better way to do this...
for _, chain := range chains {
for _, trust := range trusted {
for _, tCert := range trust.Certificates {
for _, cCert := range chain {
if tCert.Equal(cCert) {
return trust
// Search for a ParsedCert that intersects with the validated chains and any additional constraints
matches := make([]*ParsedCert, 0)
for _, trust := range trusted { // For each ParsedCert in the config
for _, tCert := range trust.Certificates { // For each certificate in the entry
for _, chain := range trustedChains { // For each root chain that we matched
for _, cCert := range chain { // For each cert in the matched chain
if tCert.Equal(cCert) && // ParsedCert intersects with matched chain
b.matchesConstraints(clientCert, chain, trust) { // validate client cert + matched chain against the config
// Add the match to the list
matches = append(matches, trust)
}
}
}
}
}
return nil
// Fail on no matches
if len(matches) == 0 {
return nil, logical.ErrorResponse("no chain matching all constraints could be found for this login certificate"), nil
}
// Return the first matching entry (for backwards compatibility, we continue to just pick one if multiple match)
return matches[0], nil, nil
}
func (b *backend) matchesConstraints(clientCert *x509.Certificate, trustedChain []*x509.Certificate, config *ParsedCert) bool {
// Default behavior (no names) is to allow all names
nameMatched := len(config.Entry.AllowedNames) == 0
// At least one pattern must match at least one name if any patterns are specified
for _, allowedName := range config.Entry.AllowedNames {
if glob.Glob(allowedName, clientCert.Subject.CommonName) {
nameMatched = true
}
for _, name := range clientCert.DNSNames {
if glob.Glob(allowedName, name) {
nameMatched = true
}
}
for _, name := range clientCert.EmailAddresses {
if glob.Glob(allowedName, name) {
nameMatched = true
}
}
}
return !b.checkForChainInCRLs(trustedChain) && nameMatched
}
// loadTrustedCerts is used to load all the trusted certificates from the backend
func (b *backend) loadTrustedCerts(store logical.Storage) (pool *x509.CertPool, trusted []*ParsedCert, trustedNonCAs []*ParsedCert) {
func (b *backend) loadTrustedCerts(store logical.Storage, certName string) (pool *x509.CertPool, trusted []*ParsedCert, trustedNonCAs []*ParsedCert) {
pool = x509.NewCertPool()
trusted = make([]*ParsedCert, 0)
trustedNonCAs = make([]*ParsedCert, 0)
@ -222,6 +247,10 @@ func (b *backend) loadTrustedCerts(store logical.Storage) (pool *x509.CertPool,
return
}
for _, name := range names {
// If we are trying to select a single CertEntry and this isn't it
if certName != "" && name != certName {
continue
}
entry, err := b.Cert(store, strings.TrimPrefix(name, "cert/"))
if err != nil {
b.Logger().Error("cert: failed to load trusted cert", "name", name, "error", err)

View File

@ -466,7 +466,7 @@ func signCert(b *backend,
}
csr, err := x509.ParseCertificateRequest(pemBlock.Bytes)
if err != nil {
return nil, errutil.UserError{Err: "certificate request could not be parsed"}
return nil, errutil.UserError{Err: fmt.Sprintf("certificate request could not be parsed: %v", err)}
}
switch role.KeyType {
@ -970,7 +970,7 @@ func createCSR(creationInfo *creationBundle) (*certutil.ParsedCSRBundle, error)
result.CSRBytes = csr
result.CSR, err = x509.ParseCertificateRequest(csr)
if err != nil {
return nil, errutil.InternalError{Err: fmt.Sprintf("unable to parse created certificate: %s", err)}
return nil, errutil.InternalError{Err: fmt.Sprintf("unable to parse created certificate: %v", err)}
}
return result, nil

View File

@ -444,7 +444,7 @@ func (c *CSRBundle) ToParsedCSRBundle() (*ParsedCSRBundle, error) {
result.CSRBytes = pemBlock.Bytes
result.CSR, err = x509.ParseCertificateRequest(result.CSRBytes)
if err != nil {
return nil, errutil.UserError{"Error encountered parsing certificate bytes from raw bundle"}
return nil, errutil.UserError{fmt.Sprintf("Error encountered parsing certificate bytes from raw bundle: %v", err)}
}
}

View File

@ -4,6 +4,8 @@ import (
"crypto/rand"
"crypto/subtle"
"fmt"
mathrand "math/rand"
"time"
)
const (
@ -166,13 +168,17 @@ func Split(secret []byte, parts, threshold int) ([][]byte, error) {
return nil, fmt.Errorf("cannot split an empty secret")
}
// Generate random list of x coordinates
mathrand.Seed(time.Now().UnixNano())
xCoordinates := mathrand.Perm(255)
// Allocate the output array, initialize the final byte
// of the output with the offset. The representation of each
// output is {y1, y2, .., yN, x}.
out := make([][]byte, parts)
for idx := range out {
out[idx] = make([]byte, len(secret)+1)
out[idx][len(secret)] = uint8(idx) + 1
out[idx][len(secret)] = uint8(xCoordinates[idx]) + 1
}
// Construct a random polynomial for each byte of the secret.
@ -189,7 +195,7 @@ func Split(secret []byte, parts, threshold int) ([][]byte, error) {
// We cheat by encoding the x value once as the final index,
// so that it only needs to be stored once.
for i := 0; i < parts; i++ {
x := uint8(i) + 1
x := uint8(xCoordinates[i]) + 1
y := p.evaluate(x)
out[i][idx] = y
}

21
vendor/github.com/ryanuber/go-glob/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Ryan Uber
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.

29
vendor/github.com/ryanuber/go-glob/README.md generated vendored Normal file
View File

@ -0,0 +1,29 @@
# String globbing in golang [![Build Status](https://travis-ci.org/ryanuber/go-glob.svg)](https://travis-ci.org/ryanuber/go-glob)
`go-glob` is a single-function library implementing basic string glob support.
Globs are an extremely user-friendly way of supporting string matching without
requiring knowledge of regular expressions or Go's particular regex engine. Most
people understand that if you put a `*` character somewhere in a string, it is
treated as a wildcard. Surprisingly, this functionality isn't found in Go's
standard library, except for `path.Match`, which is intended to be used while
comparing paths (not arbitrary strings), and contains specialized logic for this
use case. A better solution might be a POSIX basic (non-ERE) regular expression
engine for Go, which doesn't exist currently.
Example
=======
```
package main
import "github.com/ryanuber/go-glob"
func main() {
glob.Glob("*World!", "Hello, World!") // true
glob.Glob("Hello,*", "Hello, World!") // true
glob.Glob("*ello,*", "Hello, World!") // true
glob.Glob("World!", "Hello, World!") // false
glob.Glob("/home/*", "/home/ryanuber/.bashrc") // true
}
```

56
vendor/github.com/ryanuber/go-glob/glob.go generated vendored Normal file
View File

@ -0,0 +1,56 @@
package glob
import "strings"
// The character which is treated like a glob
const GLOB = "*"
// Glob will test a string pattern, potentially containing globs, against a
// subject string. The result is a simple true/false, determining whether or
// not the glob pattern matched the subject text.
func Glob(pattern, subj string) bool {
// Empty pattern can only match empty subject
if pattern == "" {
return subj == pattern
}
// If the pattern _is_ a glob, it matches everything
if pattern == GLOB {
return true
}
parts := strings.Split(pattern, GLOB)
if len(parts) == 1 {
// No globs in pattern, so test for equality
return subj == pattern
}
leadingGlob := strings.HasPrefix(pattern, GLOB)
trailingGlob := strings.HasSuffix(pattern, GLOB)
end := len(parts) - 1
// Go over the leading parts and ensure they match.
for i := 0; i < end; i++ {
idx := strings.Index(subj, parts[i])
switch i {
case 0:
// Check the first section. Requires special handling.
if !leadingGlob && idx != 0 {
return false
}
default:
// Check that the middle parts match.
if idx < 0 {
return false
}
}
// Trim evaluated text from subj as we loop over the pattern.
subj = subj[idx+len(parts[i]):]
}
// Reached the last section. Requires special handling.
return trailingGlob || strings.HasSuffix(subj, parts[end])
}

View File

@ -5,7 +5,6 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
)
@ -84,7 +83,7 @@ func (c *Client) call(endpoint, method string, request, response interface{}) er
var url = "https://" + c.org + "." + c.Url + "/api/v1/" + endpoint
req, err := http.NewRequest(method, url, bytes.NewBuffer(data))
if err != nil {
log.Fatal(err)
return err
}
req.Header.Add("Accept", `application/json`)
@ -95,19 +94,19 @@ func (c *Client) call(endpoint, method string, request, response interface{}) er
resp, err := c.client.Do(req)
if err != nil {
log.Fatal(err)
return err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
return err
}
if resp.StatusCode == http.StatusOK {
err := json.Unmarshal(body, &response)
if err != nil {
log.Fatal(err)
return err
}
} else {
var errors ErrorResponse

12
vendor/vendor.json vendored
View File

@ -1158,6 +1158,12 @@
"revision": "ddeb643de91b4ee0d9d87172c931a4ea3d81d49a",
"revisionTime": "2017-02-08T17:17:27Z"
},
{
"checksumSHA1": "6JP37UqrI0H80Gpk0Y2P+KXgn5M=",
"path": "github.com/ryanuber/go-glob",
"revision": "256dc444b735e061061cf46c809487313d5b0065",
"revisionTime": "2017-01-28T01:21:29Z"
},
{
"checksumSHA1": "5SYLEhADhdBVZAGPVHWggQl7H8k=",
"path": "github.com/samuel/go-zookeeper/zk",
@ -1171,10 +1177,10 @@
"revisionTime": "2017-04-08T21:24:09Z"
},
{
"checksumSHA1": "reJ+wO9qzH/7r2vXQE5MiTvg8+w=",
"checksumSHA1": "7b7psq20O8IOCr885W2Ld6a3KTc=",
"path": "github.com/sstarcher/go-okta",
"revision": "388b6aef4eed400621bd3e3a98d831ef1368582d",
"revisionTime": "2016-10-03T17:19:47Z"
"revision": "64b3cb9e3a7b6d0c4e4432576c873e492d152666",
"revisionTime": "2017-04-28T20:44:25Z"
},
{
"checksumSHA1": "MxLnUmfrP+r5HfCZM29+WPKebn8=",

View File

@ -15,6 +15,7 @@ var (
Version = "unknown"
VersionPrerelease = "unknown"
VersionMetadata = ""
)
// VersionInfo
@ -22,11 +23,13 @@ type VersionInfo struct {
Revision string
Version string
VersionPrerelease string
VersionMetadata string
}
func GetVersion() *VersionInfo {
ver := Version
rel := VersionPrerelease
md := VersionMetadata
if GitDescribe != "" {
ver = GitDescribe
}
@ -38,6 +41,7 @@ func GetVersion() *VersionInfo {
Revision: GitCommit,
Version: ver,
VersionPrerelease: rel,
VersionMetadata: md,
}
}
@ -52,6 +56,10 @@ func (c *VersionInfo) VersionNumber() string {
version = fmt.Sprintf("%s-%s", version, c.VersionPrerelease)
}
if c.VersionMetadata != "" {
version = fmt.Sprintf("%s+%s", version, c.VersionMetadata)
}
return version
}
@ -66,6 +74,11 @@ func (c *VersionInfo) FullVersionNumber(rev bool) string {
if c.VersionPrerelease != "" {
fmt.Fprintf(&versionString, "-%s", c.VersionPrerelease)
}
if c.VersionMetadata != "" {
fmt.Fprintf(&versionString, "+%s", c.VersionMetadata)
}
if rev && c.Revision != "" {
fmt.Fprintf(&versionString, " (%s)", c.Revision)
}

View File

@ -71,6 +71,13 @@ single word name or a more complex, nested path.
- `type` `(string: <required>)`  Specifies the type of the audit backend.
Additionally, the following options are allowed in Vault open-source, but
relevant functionality is only supported in Vault Enterprise:
- `local` `(bool: false)` Specifies if the audit backend is a local mount
only. Local mounts are not replicated nor (if a secondary) removed by
replication.
### Sample Payload
```json

View File

@ -74,6 +74,13 @@ For example, mounting the "foo" auth backend will make it accessible at
- `type` `(string: <required>)`  Specifies the name of the authentication
backend type, such as "github" or "token".
Additionally, the following options are allowed in Vault open-source, but
relevant functionality is only supported in Vault Enterprise:
- `local` `(bool: false)` Specifies if the auth backend is a local mount
only. Local mounts are not replicated nor (if a secondary) removed by
replication.
### Sample Payload
```json

View File

@ -84,6 +84,13 @@ This endpoint mounts a new secret backend at the given path.
disabling backend caching respectively. If set on a specific mount, this
overrides the global defaults.
Additionally, the following options are allowed in Vault open-source, but
relevant functionality is only supported in Vault Enterprise:
- `local` `(bool: false)` Specifies if the secret backend is a local mount
only. Local mounts are not replicated nor (if a secondary) removed by
replication.
### Sample Payload
```json

View File

@ -41,6 +41,7 @@ token.
```
$ curl \
--header "X-Vault-Token: ..." \
--header "X-Vault-Wrap-TTL: 60" \
--request POST \
--data @payload.json \
https://vault.rocks/v1/sys/wrapping/wrap

View File

@ -60,18 +60,25 @@ it is up to the administrator to remove it from the backend.
## Authentication
### Via the CLI
The below requires Vault to present a certificate signed by `ca.pem` and
presents `cert.pem` (using `key.pem`) to authenticate against the `web` cert
role. If a certificate role name is not specified, the auth backend will try to
authenticate against all trusted certificates.
```
$ vault auth -method=cert \
-ca-cert=ca.pem -client-cert=cert.pem -client-key=key.pem
-ca-cert=ca.pem -client-cert=cert.pem -client-key=key.pem \
name=web
```
### Via the API
The endpoint for the login is `/login`. The client simply connects with their TLS
certificate and when the login endpoint is hit, the auth backend will determine
if there is a matching trusted certificate to authenticate the client.
if there is a matching trusted certificate to authenticate the client. Optionally,
you may specify a single certificate role to authenticate against.
```
$ curl --cacert ca.pem --cert cert.pem --key key.pem \
$ curl --cacert ca.pem --cert cert.pem --key key.pem -d name=web \
$VAULT_ADDR/v1/auth/cert/login -XPOST
```
@ -175,6 +182,7 @@ of the header should be "X-Vault-Token" and the value should be the token.
"certificate": "-----BEGIN CERTIFICATE-----\nMIIEtzCCA5+.......ZRtAfQ6r\nwlW975rYa1ZqEdA=\n-----END CERTIFICATE-----",
"display_name": "test",
"policies": "",
"allowed_names": "",
"ttl": 2764800
},
"warnings": null,
@ -245,6 +253,15 @@ of the header should be "X-Vault-Token" and the value should be the token.
<span class="param-flags">required</span>
The PEM-format CA certificate.
</li>
<li>
<span class="param">allowed_names</span>
<span class="param-flags">optional</span>
Constrain the Common and Alternative Names in the client certificate
with a [globbed pattern](https://github.com/ryanuber/go-glob/blob/master/README.md#example).
Value is a comma-separated list of patterns.
Authentication requires at least one Name matching at least one pattern.
If not set, defaults to allowing all names.
</li>
<li>
<span class="param">policies</span>
<span class="param-flags">optional</span>
@ -382,8 +399,8 @@ of the header should be "X-Vault-Token" and the value should be the token.
<dl class="api">
<dt>Description</dt>
<dd>
Log in and fetch a token. If there is a valid chain to a CA configured in the backend,
a token will be issued.
Log in and fetch a token. If there is a valid chain to a CA configured in
the backend and all role constraints are matched, a token will be issued.
</dd>
<dt>Method</dt>
@ -394,7 +411,15 @@ of the header should be "X-Vault-Token" and the value should be the token.
<dt>Parameters</dt>
<dd>
None.
<ul>
<li>
<span class="param">name</span>
<span class="param-flags">optional</span>
Authenticate against only the named certificate role, returning its
policy list if successful. If not set, defaults to trying all
certificate roles and returning any one that matches.
</li>
</ul>
</dd>
<dt>Returns</dt>

View File

@ -193,6 +193,6 @@ storage "consul" {
```
[consul]: https://www.consul.io/ "Consul by HashiCorp"
[consul-acl]: https://www.consul.io/docs/internals/acl.html "Consul ACLs"
[consul-consistency]: https://www.consul.io/docs/agent/http.html#consistency-modes "Consul Consistency Modes"
[consul-acl]: https://www.consul.io/docs/guides/acl.html "Consul ACLs"
[consul-consistency]: https://www.consul.io/api/index.html#consistency-modes "Consul Consistency Modes"
[consul-encryption]: https://www.consul.io/docs/agent/encryption.html "Consul Encryption"