Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make PKI root generation idempotent-ish and add delete endpoint. #3165

Merged
merged 4 commits into from
Aug 15, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion builtin/logical/pki/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ func Backend() *backend {
pathListRoles(&b),
pathRoles(&b),
pathGenerateRoot(&b),
pathSignIntermediate(&b),
pathDeleteRoot(&b),
pathGenerateIntermediate(&b),
pathSetSignedIntermediate(&b),
pathSignIntermediate(&b),
pathConfigCA(&b),
pathConfigCRL(&b),
pathConfigURLs(&b),
Expand Down
78 changes: 78 additions & 0 deletions builtin/logical/pki/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,13 @@ import (
"time"

"github.com/fatih/structs"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/helper/certutil"
"github.com/hashicorp/vault/helper/strutil"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/logical"
logicaltest "github.com/hashicorp/vault/logical/testing"
"github.com/hashicorp/vault/vault"
"github.com/mitchellh/mapstructure"
)

Expand Down Expand Up @@ -2156,6 +2159,81 @@ func TestBackend_SignVerbatim(t *testing.T) {
}
}

func TestBackend_Root_Idempotentcy(t *testing.T) {
coreConfig := &vault.CoreConfig{
LogicalBackends: map[string]logical.Factory{
"pki": Factory,
},
}
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
HandlerFunc: vaulthttp.Handler,
})
cluster.Start()
defer cluster.Cleanup()

client := cluster.Cores[0].Client
var err error
err = client.Sys().Mount("pki", &api.MountInput{
Type: "pki",
Config: api.MountConfigInput{
DefaultLeaseTTL: "16h",
MaxLeaseTTL: "32h",
},
})
if err != nil {
t.Fatal(err)
}

resp, err := client.Logical().Write("pki/root/generate/internal", map[string]interface{}{
"common_name": "myvault.com",
})
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("expected ca info")
}
resp, err = client.Logical().Read("pki/cert/ca_chain")
r1Data := resp.Data

// Try again, make sure it's a 204 and same CA
resp, err = client.Logical().Write("pki/root/generate/internal", map[string]interface{}{
"common_name": "myvault.com",
})
if err != nil {
t.Fatal(err)
}
if resp != nil {
t.Fatal("expected no ca info")
}
resp, err = client.Logical().Read("pki/cert/ca_chain")
r2Data := resp.Data
if !reflect.DeepEqual(r1Data, r2Data) {
t.Fatal("got different ca certs")
}

resp, err = client.Logical().Delete("pki/root")
if err != nil {
t.Fatal(err)
}
if resp != nil {
t.Fatal("expected nil response")
}
// Make sure it behaves the same
resp, err = client.Logical().Delete("pki/root")
if err != nil {
t.Fatal(err)
}
if resp != nil {
t.Fatal("expected nil response")
}

_, err = client.Logical().Read("pki/cert/ca_chain")
if err == nil {
t.Fatal("expected error")
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a test step here which tries to generate a root after the deletion?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


const (
rsaCAKey string = `-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAmPQlK7xD5p+E8iLQ8XlVmll5uU2NKMxKY3UF5tbh+0vkc+Fy
Expand Down
4 changes: 1 addition & 3 deletions builtin/logical/pki/path_config_ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ func pathConfigCA(b *backend) *framework.Path {
"pem_bundle": &framework.FieldSchema{
Type: framework.TypeString,
Description: `PEM-format, concatenated unencrypted
secret key and certificate, or, if a
CSR was generated with the "generate"
endpoint, just the signed certificate.`,
secret key and certificate.`,
},
},

Expand Down
4 changes: 3 additions & 1 deletion builtin/logical/pki/path_fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ func (b *backend) pathFetchRead(req *logical.Request, data *framework.FieldData)
caInfo, err := fetchCAInfo(req)
switch err.(type) {
case errutil.UserError:
response = logical.ErrorResponse(funcErr.Error())
response = logical.ErrorResponse(err.Error())
goto reply
case errutil.InternalError:
retErr = err
Expand Down Expand Up @@ -244,6 +244,8 @@ reply:
}
case retErr != nil:
response = nil
case response.IsError():
return response, nil
default:
response.Data["certificate"] = string(certificate)
response.Data["revocation_time"] = revocationTime
Expand Down
38 changes: 37 additions & 1 deletion builtin/logical/pki/path_root.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,21 @@ func pathGenerateRoot(b *backend) *framework.Path {
return ret
}

func pathDeleteRoot(b *backend) *framework.Path {
ret := &framework.Path{
Pattern: "root",

Callbacks: map[logical.Operation]framework.OperationFunc{
logical.DeleteOperation: b.pathCADeleteRoot,
},

HelpSynopsis: pathDeleteRootHelpSyn,
HelpDescription: pathDeleteRootHelpDesc,
}

return ret
}

func pathSignIntermediate(b *backend) *framework.Path {
ret := &framework.Path{
Pattern: "root/sign-intermediate",
Expand Down Expand Up @@ -66,10 +81,23 @@ the non-repudiation flag.`,
return ret
}

func (b *backend) pathCADeleteRoot(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
return nil, req.Storage.Delete("config/ca_bundle")
}

func (b *backend) pathCAGenerateRoot(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
var err error

entry, err := req.Storage.Get("config/ca_bundle")
if err != nil {
return nil, err
}
if entry != nil {
return nil, nil
}

exported, format, role, errorResp := b.getGenerationParams(data)
if errorResp != nil {
return errorResp, nil
Expand Down Expand Up @@ -133,7 +161,7 @@ func (b *backend) pathCAGenerateRoot(
}

// Store it as the CA bundle
entry, err := logical.StorageEntryJSON("config/ca_bundle", cb)
entry, err = logical.StorageEntryJSON("config/ca_bundle", cb)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -299,6 +327,14 @@ const pathGenerateRootHelpDesc = `
See the API documentation for more information.
`

const pathDeleteRootHelpSyn = `
Deletes the root CA key to allow a new one to be generated.
`

const pathDeleteRootHelpDesc = `
See the API documentation for more information.
`

const pathSignIntermediateHelpSyn = `
Issue an intermediate CA certificate based on the provided CSR.
`
Expand Down
28 changes: 23 additions & 5 deletions website/source/api/secret/pki/index.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ update your API calls accordingly.
## Read CA Certificate

This endpoint retrieves the CA certificate *in raw DER-encoded form*. This is a
bare endpoint that does not return a standard Vault data structure. If `/pem` is
added to the endpoint, the CA certificate is returned in PEM format.
bare endpoint that does not return a standard Vault data structure and cannot
be read by the Vault CLI. If `/pem` is added to the endpoint, the CA
certificate is returned in PEM format.

This is an unauthenticated endpoint.

Expand All @@ -73,7 +74,7 @@ $ curl \

This endpoint retrieves the CA certificate chain, including the CA _in PEM
format_. This is a bare endpoint that does not return a standard Vault data
structure.
structure and cannot be read by the Vault CLI.

This is an unauthenticated endpoint.

Expand Down Expand Up @@ -460,8 +461,6 @@ $ curl \
https://vault.rocks/v1/pki/intermediate/generate/internal
```

### Sample Response

```json
{
"lease_id": "",
Expand Down Expand Up @@ -974,6 +973,25 @@ $ curl \
}
```

## Delete Root

This endpoint deletes the current CA key (the old CA certificate will still be
accessible for reading until a new certificate/key are generated or uploaded).

| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `DELETE` | `/pki/root` | `204 (empty body)` |


### Sample Request

```
$ curl \
--header "X-Vault-Token: ..." \
--request DELETE \
https://vault.rocks/v1/pki/root
```

## Sign Intermediate

This endpoint uses the configured CA certificate to issue a certificate with
Expand Down