Skip to content

Commit

Permalink
Force new root CA creation on out of band changes
Browse files Browse the repository at this point in the history
  • Loading branch information
benashz committed May 3, 2022
1 parent 58e9721 commit 7067d08
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 44 deletions.
6 changes: 5 additions & 1 deletion util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ func ToStringArray(input []interface{}) []string {
}

func Is404(err error) bool {
return strings.Contains(err.Error(), "Code: 404")
return IsHTTPErrorCode(err, http.StatusNotFound)
}

func IsHTTPErrorCode(err error, code int) bool {
return strings.Contains(err.Error(), fmt.Sprintf("Code: %d", code))
}

func CalculateConflictsWith(self string, group []string) []string {
Expand Down
82 changes: 74 additions & 8 deletions vault/resource_pki_secret_backend_root_cert.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,59 @@
package vault

import (
"context"
"crypto/x509"
"encoding/pem"
"fmt"
"io"
"log"
"net/http"
"strings"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/sdk/helper/certutil"

"github.com/hashicorp/terraform-provider-vault/util"
)

func pkiSecretBackendRootCertResource() *schema.Resource {
return &schema.Resource{
Create: pkiSecretBackendRootCertCreate,
Read: pkiSecretBackendRootCertRead,
Update: pkiSecretBackendRootCertUpdate,
Delete: pkiSecretBackendRootCertDelete,
Update: func(data *schema.ResourceData, i interface{}) error {
return nil
},
Read: func(data *schema.ResourceData, i interface{}) error {
return nil
},
CustomizeDiff: func(_ context.Context, d *schema.ResourceDiff, meta interface{}) error {
client := meta.(*api.Client)
cert, err := getCACertificate(client, d.Get("backend").(string))
if err != nil {
return err
}

if cert != nil {
key := "serial"
cur := d.Get(key).(string)
n := certutil.GetHexFormatted(cert.SerialNumber.Bytes(), ":")
if err := d.SetNew(key, n); err != nil {
return err
}

o, _ := d.GetChange(key)
// don't force new on new resources
if o.(string) != "" && cur != n {
if err := d.ForceNew(key); err != nil {
return err
}
}

}
return nil
},

Schema: map[string]*schema.Schema{
"backend": {
Expand Down Expand Up @@ -177,7 +215,7 @@ func pkiSecretBackendRootCertResource() *schema.Resource {
"certificate": {
Type: schema.TypeString,
Computed: true,
Description: "The certicate.",
Description: "The certificate.",
},
"issuing_ca": {
Type: schema.TypeString,
Expand Down Expand Up @@ -281,15 +319,43 @@ func pkiSecretBackendRootCertCreate(d *schema.ResourceData, meta interface{}) er
d.Set("serial", resp.Data["serial_number"])

d.SetId(path)
return pkiSecretBackendRootCertRead(d, meta)
}

func pkiSecretBackendRootCertRead(d *schema.ResourceData, meta interface{}) error {
return nil
}

func pkiSecretBackendRootCertUpdate(d *schema.ResourceData, m interface{}) error {
return nil
func getCACertificate(client *api.Client, mount string) (*x509.Certificate, error) {
path := fmt.Sprintf("/v1/%s/ca/pem", mount)
req := client.NewRequest(http.MethodGet, path)
req.ClientToken = ""
resp, err := client.RawRequest(req)
if err != nil {
if util.IsHTTPErrorCode(err, http.StatusNotFound) || util.IsHTTPErrorCode(err, http.StatusForbidden) {
return nil, nil
}
return nil, err
}

if resp == nil {
return nil, fmt.Errorf("expected a response body, got nil response")
}

defer resp.Body.Close()
data, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}

log.Printf("[INFO] Reading current CA")
b, _ := pem.Decode(data)
if b != nil {
cert, err := x509.ParseCertificate(b.Bytes)
if err != nil {
return nil, err
}
return cert, nil
}

return nil, nil
}

func pkiSecretBackendRootCertDelete(d *schema.ResourceData, meta interface{}) error {
Expand Down
121 changes: 86 additions & 35 deletions vault/resource_pki_secret_backend_root_cert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,78 @@ import (
func TestPkiSecretBackendRootCertificate_basic(t *testing.T) {
path := "pki-" + strconv.Itoa(acctest.RandInt())

resourceName := "vault_pki_secret_backend_root_cert.test"

checks := []resource.TestCheckFunc{
resource.TestCheckResourceAttr(resourceName, "backend", path),
resource.TestCheckResourceAttr(resourceName, "type", "internal"),
resource.TestCheckResourceAttr(resourceName, "common_name", "test Root CA"),
resource.TestCheckResourceAttr(resourceName, "ttl", "86400"),
resource.TestCheckResourceAttr(resourceName, "format", "pem"),
resource.TestCheckResourceAttr(resourceName, "private_key_format", "der"),
resource.TestCheckResourceAttr(resourceName, "key_type", "rsa"),
resource.TestCheckResourceAttr(resourceName, "key_bits", "4096"),
resource.TestCheckResourceAttr(resourceName, "ou", "test"),
resource.TestCheckResourceAttr(resourceName, "organization", "test"),
resource.TestCheckResourceAttr(resourceName, "country", "test"),
resource.TestCheckResourceAttr(resourceName, "locality", "test"),
resource.TestCheckResourceAttr(resourceName, "province", "test"),
resource.TestCheckResourceAttrSet(resourceName, "serial"),
}

resource.Test(t, resource.TestCase{
Providers: testProviders,
PreCheck: func() { testutil.TestAccPreCheck(t) },
CheckDestroy: testPkiSecretBackendRootCertificateDestroy,
Steps: []resource.TestStep{
{
Config: testPkiSecretBackendRootCertificateConfig_basic(path),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("vault_pki_secret_backend_root_cert.test", "backend", path),
resource.TestCheckResourceAttr("vault_pki_secret_backend_root_cert.test", "type", "internal"),
resource.TestCheckResourceAttr("vault_pki_secret_backend_root_cert.test", "common_name", "test Root CA"),
resource.TestCheckResourceAttr("vault_pki_secret_backend_root_cert.test", "ttl", "86400"),
resource.TestCheckResourceAttr("vault_pki_secret_backend_root_cert.test", "format", "pem"),
resource.TestCheckResourceAttr("vault_pki_secret_backend_root_cert.test", "private_key_format", "der"),
resource.TestCheckResourceAttr("vault_pki_secret_backend_root_cert.test", "key_type", "rsa"),
resource.TestCheckResourceAttr("vault_pki_secret_backend_root_cert.test", "key_bits", "4096"),
resource.TestCheckResourceAttr("vault_pki_secret_backend_root_cert.test", "ou", "test"),
resource.TestCheckResourceAttr("vault_pki_secret_backend_root_cert.test", "organization", "test"),
resource.TestCheckResourceAttr("vault_pki_secret_backend_root_cert.test", "country", "test"),
resource.TestCheckResourceAttr("vault_pki_secret_backend_root_cert.test", "locality", "test"),
resource.TestCheckResourceAttr("vault_pki_secret_backend_root_cert.test", "province", "test"),
resource.TestCheckResourceAttrSet("vault_pki_secret_backend_root_cert.test", "serial"),
),
Check: resource.ComposeTestCheckFunc(checks...),
},
{
PreConfig: func() {
client := testProvider.Meta().(*api.Client)
_, err := client.Logical().Delete(fmt.Sprintf("%s/root", path))
if err != nil {
t.Fatal(err)
}
},
Config: testPkiSecretBackendRootCertificateConfig_basic(path),
},
{
// test unmounted backend
PreConfig: func() {
client := testProvider.Meta().(*api.Client)
if err := client.Sys().Unmount(path); err != nil {
t.Fatal(err)
}
},
Config: testPkiSecretBackendRootCertificateConfig_basic(path),
},
{
// test out of band update to the root CA
PreConfig: func() {
client := testProvider.Meta().(*api.Client)
_, err := client.Logical().Delete(fmt.Sprintf("%s/root", path))
if err != nil {
t.Fatal(err)
}
genPath := pkiSecretBackendIntermediateSetSignedReadPath(path, "internal")
resp, err := client.Logical().Write(genPath,
map[string]interface{}{
"common_name": "out-of-band",
},
)
if err != nil {
t.Fatal(err)
}

if resp == nil {
t.Fatalf("empty response for write on path %s", genPath)
}
},
Config: testPkiSecretBackendRootCertificateConfig_basic(path),
Check: resource.ComposeTestCheckFunc(checks...),
},
},
})
Expand Down Expand Up @@ -69,30 +118,32 @@ func testPkiSecretBackendRootCertificateDestroy(s *terraform.State) error {
}

func testPkiSecretBackendRootCertificateConfig_basic(path string) string {
return fmt.Sprintf(`
config := fmt.Sprintf(`
resource "vault_mount" "test" {
path = "%s"
type = "pki"
description = "test"
path = "%s"
type = "pki"
description = "test"
default_lease_ttl_seconds = "86400"
max_lease_ttl_seconds = "86400"
}
resource "vault_pki_secret_backend_root_cert" "test" {
depends_on = [ "vault_mount.test" ]
backend = vault_mount.test.path
type = "internal"
common_name = "test Root CA"
ttl = "86400"
format = "pem"
private_key_format = "der"
key_type = "rsa"
key_bits = 4096
backend = vault_mount.test.path
type = "internal"
common_name = "test Root CA"
ttl = "86400"
format = "pem"
private_key_format = "der"
key_type = "rsa"
key_bits = 4096
exclude_cn_from_sans = true
ou = "test"
organization = "test"
country = "test"
locality = "test"
province = "test"
}`, path)
ou = "test"
organization = "test"
country = "test"
locality = "test"
province = "test"
}
`, path)

return config
}

0 comments on commit 7067d08

Please sign in to comment.