Merge pull request #1227 from hashicorp/issue-477

Don't renew cert-based tokens if the policies have changed.
This commit is contained in:
Jeff Mitchell 2016-03-17 18:25:39 -04:00
commit 3e3621841d
2 changed files with 131 additions and 0 deletions

View file

@ -10,6 +10,7 @@ import (
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
logicaltest "github.com/hashicorp/vault/logical/testing"
"github.com/mitchellh/mapstructure"
)
@ -383,3 +384,122 @@ func testConnState(t *testing.T, certPath, keyPath, rootCertPath string) tls.Con
connState := serverConn.(*tls.Conn).ConnectionState()
return connState
}
func Test_Renew(t *testing.T) {
storage := &logical.InmemStorage{}
lb, err := Factory(&logical.BackendConfig{
System: &logical.StaticSystemView{
DefaultLeaseTTLVal: 300 * time.Second,
MaxLeaseTTLVal: 1800 * time.Second,
},
StorageView: storage,
})
if err != nil {
t.Fatal("error: %s", err)
}
b := lb.(*backend)
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")
if err != nil {
t.Fatal(err)
}
req := &logical.Request{
Connection: &logical.Connection{
ConnState: &connState,
},
Storage: storage,
Auth: &logical.Auth{},
}
fd := &framework.FieldData{
Raw: map[string]interface{}{
"name": "test",
"certificate": ca,
"policies": "foo,bar",
},
Schema: pathCerts(b).Fields,
}
resp, err := b.pathCertWrite(req, fd)
if err != nil {
t.Fatal(err)
}
resp, err = b.pathLogin(req, nil)
if err != nil {
t.Fatal(err)
}
req.Auth.InternalData = resp.Auth.InternalData
req.Auth.Metadata = resp.Auth.Metadata
req.Auth.LeaseOptions = resp.Auth.LeaseOptions
req.Auth.IssueTime = time.Now()
// Normal renewal
resp, err = b.pathLoginRenew(req, nil)
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("got nil response from renew")
}
if resp.IsError() {
t.Fatalf("got error: %#v", *resp)
}
// Change the policies -- this should fail
fd.Raw["policies"] = "zip,zap"
resp, err = b.pathCertWrite(req, fd)
if err != nil {
t.Fatal(err)
}
resp, err = b.pathLoginRenew(req, nil)
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("got nil response from renew")
}
if !resp.IsError() {
t.Fatal("expected error")
}
// Put the policies back, this shold be okay
fd.Raw["policies"] = "bar,foo"
resp, err = b.pathCertWrite(req, fd)
if err != nil {
t.Fatal(err)
}
resp, err = b.pathLoginRenew(req, nil)
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("got nil response from renew")
}
if resp.IsError() {
t.Fatal("got error: %#v", *resp)
}
// Delete CA, make sure we can't renew
resp, err = b.pathCertDelete(req, fd)
if err != nil {
t.Fatal(err)
}
resp, err = b.pathLoginRenew(req, nil)
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("got nil response from renew")
}
if !resp.IsError() {
t.Fatal("expected error")
}
}

View file

@ -7,6 +7,7 @@ import (
"encoding/base64"
"encoding/pem"
"errors"
"sort"
"strings"
"github.com/hashicorp/vault/helper/certutil"
@ -58,12 +59,16 @@ func (b *backend) pathLogin(
skid := base64.StdEncoding.EncodeToString(clientCerts[0].SubjectKeyId)
akid := base64.StdEncoding.EncodeToString(clientCerts[0].AuthorityKeyId)
// We want to sort here so we can check properly during renewal)
sort.Strings(matched.Entry.Policies)
// Generate a response
resp := &logical.Response{
Auth: &logical.Auth{
InternalData: map[string]interface{}{
"subject_key_id": skid,
"authority_key_id": akid,
"policies": strings.Join(matched.Entry.Policies, ","),
},
Policies: matched.Entry.Policies,
DisplayName: matched.Entry.DisplayName,
@ -127,6 +132,12 @@ func (b *backend) pathLoginRenew(
return nil, nil
}
policies := cert.Policies
sort.Strings(policies)
if strings.Join(policies, ",") != req.Auth.InternalData["policies"] {
return logical.ErrorResponse("policies have changed, not renewing"), nil
}
return framework.LeaseExtend(cert.TTL, 0, b.System())(req, d)
}