Skip to content

Commit

Permalink
Merge branch 'master-oss' into read-lease
Browse files Browse the repository at this point in the history
  • Loading branch information
Chris Hoffman committed Apr 30, 2017
2 parents 585464a + a05fee0 commit 9ac7d8d
Show file tree
Hide file tree
Showing 20 changed files with 368 additions and 88 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
90 changes: 73 additions & 17 deletions builtin/credential/cert/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"})...)
Expand All @@ -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),
},
})
}
Expand Down Expand Up @@ -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")
Expand All @@ -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")
Expand Down Expand Up @@ -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",
Expand All @@ -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,
},
}
}

Expand All @@ -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",
Expand All @@ -521,6 +566,9 @@ func testAccStepLoginInvalid(t *testing.T, connState tls.ConnectionState) logica
}
return nil
},
Data: map[string]interface{}{
"name": certName,
},
ErrorOk: true,
}
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -730,18 +779,25 @@ 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
req.Auth.Policies = resp.Auth.Policies
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)
}
Expand All @@ -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")
}
Expand All @@ -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)
}
Expand All @@ -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)
}
Expand Down
9 changes: 8 additions & 1 deletion builtin/credential/cert/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
Expand All @@ -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
`

Expand Down
27 changes: 18 additions & 9 deletions builtin/credential/cert/path_certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 == "" {
Expand All @@ -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
Expand Down Expand Up @@ -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 = `
Expand Down
Loading

0 comments on commit 9ac7d8d

Please sign in to comment.