Skip to content

Commit

Permalink
Update sign-verbatim to correctly set generate_lease (#2593)
Browse files Browse the repository at this point in the history
  • Loading branch information
jefferai committed Apr 18, 2017
1 parent 8b5097f commit 85b9281
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 7 deletions.
166 changes: 166 additions & 0 deletions builtin/logical/pki/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1984,6 +1984,172 @@ func TestBackend_PathFetchCertList(t *testing.T) {
}
}

func TestBackend_SignVerbatim(t *testing.T) {
// create the backend
config := logical.TestBackendConfig()
storage := &logical.InmemStorage{}
config.StorageView = storage

b := Backend()
_, err := b.Setup(config)
if err != nil {
t.Fatal(err)
}

// generate root
rootData := map[string]interface{}{
"common_name": "test.com",
"ttl": "172800",
}

resp, err := b.HandleRequest(&logical.Request{
Operation: logical.UpdateOperation,
Path: "root/generate/internal",
Storage: storage,
Data: rootData,
})
if resp != nil && resp.IsError() {
t.Fatalf("failed to generate root, %#v", *resp)
}
if err != nil {
t.Fatal(err)
}

// create a CSR and key
key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatal(err)
}
csrReq := &x509.CertificateRequest{
Subject: pkix.Name{
CommonName: "foo.bar.com",
},
}
csr, err := x509.CreateCertificateRequest(rand.Reader, csrReq, key)
if err != nil {
t.Fatal(err)
}
if len(csr) == 0 {
t.Fatal("generated csr is empty")
}
pemCSR := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE REQUEST",
Bytes: csr,
})
if len(pemCSR) == 0 {
t.Fatal("pem csr is empty")
}

resp, err = b.HandleRequest(&logical.Request{
Operation: logical.UpdateOperation,
Path: "sign-verbatim",
Storage: storage,
Data: map[string]interface{}{
"csr": string(pemCSR),
},
})
if resp != nil && resp.IsError() {
t.Fatalf("failed to sign-verbatim basic CSR: %#v", *resp)
}
if err != nil {
t.Fatal(err)
}
if resp.Secret != nil {
t.Fatal("secret is not nil")
}

// create a role entry; we use this to check that sign-verbatim when used with a role is still honoring TTLs
roleData := map[string]interface{}{
"ttl": "4h",
"max_ttl": "8h",
}
resp, err = b.HandleRequest(&logical.Request{
Operation: logical.UpdateOperation,
Path: "roles/test",
Storage: storage,
Data: roleData,
})
if resp != nil && resp.IsError() {
t.Fatalf("failed to create a role, %#v", *resp)
}
if err != nil {
t.Fatal(err)
}
resp, err = b.HandleRequest(&logical.Request{
Operation: logical.UpdateOperation,
Path: "sign-verbatim/test",
Storage: storage,
Data: map[string]interface{}{
"csr": string(pemCSR),
"ttl": "5h",
},
})
if resp != nil && resp.IsError() {
t.Fatalf("failed to sign-verbatim ttl'd CSR: %#v", *resp)
}
if err != nil {
t.Fatal(err)
}
if resp.Secret != nil {
t.Fatal("got a lease when we should not have")
}
resp, err = b.HandleRequest(&logical.Request{
Operation: logical.UpdateOperation,
Path: "sign-verbatim/test",
Storage: storage,
Data: map[string]interface{}{
"csr": string(pemCSR),
"ttl": "12h",
},
})
if resp != nil && !resp.IsError() {
t.Fatalf("sign-verbatim signed too-large-ttl'd CSR: %#v", *resp)
}
if err != nil {
t.Fatal(err)
}

// now check that if we set generate-lease it takes it from the role and the TTLs match
roleData = map[string]interface{}{
"ttl": "4h",
"max_ttl": "8h",
"generate_lease": true,
}
resp, err = b.HandleRequest(&logical.Request{
Operation: logical.UpdateOperation,
Path: "roles/test",
Storage: storage,
Data: roleData,
})
if resp != nil && resp.IsError() {
t.Fatalf("failed to create a role, %#v", *resp)
}
if err != nil {
t.Fatal(err)
}
resp, err = b.HandleRequest(&logical.Request{
Operation: logical.UpdateOperation,
Path: "sign-verbatim/test",
Storage: storage,
Data: map[string]interface{}{
"csr": string(pemCSR),
"ttl": "5h",
},
})
if resp != nil && resp.IsError() {
t.Fatalf("failed to sign-verbatim role-leased CSR: %#v", *resp)
}
if err != nil {
t.Fatal(err)
}
if resp.Secret == nil {
t.Fatalf("secret is nil, response is %#v", *resp)
}
if math.Abs(float64(resp.Secret.TTL-(5*time.Hour))) > float64(5*time.Hour) {
t.Fatalf("ttl not default; wanted %v, got %v", b.System().DefaultLeaseTTL(), resp.Secret.TTL)
}
}

const (
rsaCAKey string = `-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAmPQlK7xD5p+E8iLQ8XlVmll5uU2NKMxKY3UF5tbh+0vkc+Fy
Expand Down
5 changes: 3 additions & 2 deletions builtin/logical/pki/cert_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (

"github.com/hashicorp/vault/helper/certutil"
"github.com/hashicorp/vault/helper/errutil"
"github.com/hashicorp/vault/helper/parseutil"
"github.com/hashicorp/vault/helper/strutil"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
Expand Down Expand Up @@ -695,7 +696,7 @@ func generateCreationBundle(b *backend,
if len(ttlField) == 0 {
ttl = b.System().DefaultLeaseTTL()
} else {
ttl, err = time.ParseDuration(ttlField)
ttl, err = parseutil.ParseDurationSecond(ttlField)
if err != nil {
return nil, errutil.UserError{Err: fmt.Sprintf(
"invalid requested ttl: %s", err)}
Expand All @@ -705,7 +706,7 @@ func generateCreationBundle(b *backend,
if len(role.MaxTTL) == 0 {
maxTTL = b.System().MaxLeaseTTL()
} else {
maxTTL, err = time.ParseDuration(role.MaxTTL)
maxTTL, err = parseutil.ParseDurationSecond(role.MaxTTL)
if err != nil {
return nil, errutil.UserError{Err: fmt.Sprintf(
"invalid ttl: %s", err)}
Expand Down
31 changes: 29 additions & 2 deletions builtin/logical/pki/path_issue_sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,19 +116,46 @@ func (b *backend) pathSign(
func (b *backend) pathSignVerbatim(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {

roleName := data.Get("role").(string)

// Get the role if one was specified
role, err := b.getRole(req.Storage, roleName)
if err != nil {
return nil, err
}

ttl := b.System().DefaultLeaseTTL()
role := &roleEntry{
maxTTL := b.System().MaxLeaseTTL()

entry := &roleEntry{
TTL: ttl.String(),
MaxTTL: maxTTL.String(),
AllowLocalhost: true,
AllowAnyName: true,
AllowIPSANs: true,
EnforceHostnames: false,
KeyType: "any",
UseCSRCommonName: true,
UseCSRSANs: true,
GenerateLease: new(bool),
}

if role != nil {
if role.TTL != "" {
entry.TTL = role.TTL
}
if role.MaxTTL != "" {
entry.MaxTTL = role.MaxTTL
}
entry.NoStore = role.NoStore
}

*entry.GenerateLease = false
if role != nil && role.GenerateLease != nil {
*entry.GenerateLease = *role.GenerateLease
}

return b.pathIssueSignCert(req, data, role, true, true)
return b.pathIssueSignCert(req, data, entry, true, true)
}

func (b *backend) pathIssueSignCert(
Expand Down
10 changes: 7 additions & 3 deletions website/source/api/secret/pki/index.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -1111,12 +1111,16 @@ refuse to issue an intermediate CA certificate (see the
**This is a potentially dangerous endpoint and only highly trusted users should
have access.**

| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/pki/sign-verbatim` | `200 application/json` |
| Method | Path | Produces |
| :------- | :----------------------------------- | :--------------------- |
| `POST` | `/pki/sign-verbatim(/:name)` | `200 application/json` |

### Parameters

- `name` `(string: "")` - Specifies a role. If set, the following parameters
from the role will have effect: `ttl`, `max_ttl`, `generate_lease`, and
`no_store`.

- `csr` `(string: <required>)` – Specifies the PEM-encoded CSR.

- `ttl` `(string: "")` – Specifies the requested Time To Live. Cannot be greater
Expand Down

0 comments on commit 85b9281

Please sign in to comment.