Vault 11798 vault cli issue intermediate (#18467)
* The verify-sign command in it's cleanest existing form. * Working state * Updates to proper verification syntax Co-authored-by: 'Alex Scheel' <alex.scheel@hashicorp.com> * make fmt * Git CI caught some stuff. * Base functionality. * make fmt; changelog * pki issue command. * Make fmt. Changelog. * Error Handling Is Almost A Tutorial * What I thought empty issuers response fix would be. * Some tests * PR-review updates. * make fmt. * Fix null response data for listing empty issuers causing a crash. * Update command/pki_list_children_command.go Fix double specifier Co-authored-by: Steven Clark <steven.clark@hashicorp.com> * Add test for pki_list_children. * Fix tests. * Update descriptions for correctness based on PR reviews. * make fmt. * Updates based on PR feedback. * Allow multiple arguements (space separated) * Remove bad merge-thing. * White-space hell fix change. * Tests, and return information for issue ca * Fix make fmt error introduced here: https://github.com/hashicorp/vault/pull/18876 * Update command/pki_issue_intermediate.go Puncutation. Co-authored-by: Alexander Scheel <alex.scheel@hashicorp.com> * Remove smart quotes for standard quotes. * More information as part of the help text. * Better help text. * Add missing "/" into error message. --------- Co-authored-by: 'Alex Scheel' <alex.scheel@hashicorp.com> Co-authored-by: Steven Clark <steven.clark@hashicorp.com>
This commit is contained in:
parent
da325bef31
commit
5ece71109a
|
@ -0,0 +1,3 @@
|
|||
```release-note:improvement
|
||||
cli/pki: Add pki issue command, which creates a CSR, has a vault mount sign it, then reimports it.
|
||||
```
|
|
@ -543,6 +543,11 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"pki issue": func() (cli.Command, error) {
|
||||
return &PKIIssueCACommand{
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"pki list-intermediates": func() (cli.Command, error) {
|
||||
return &PKIListIntermediateCommand{
|
||||
BaseCommand: getBaseCommand(),
|
||||
|
|
|
@ -0,0 +1,355 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
paths "path"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/vault/api"
|
||||
"github.com/posener/complete"
|
||||
)
|
||||
|
||||
type PKIIssueCACommand struct {
|
||||
*BaseCommand
|
||||
|
||||
flagConfig string
|
||||
flagReturnIndicator string
|
||||
flagDefaultDisabled bool
|
||||
flagList bool
|
||||
|
||||
flagKeyStorageSource string
|
||||
flagNewIssuerName string
|
||||
}
|
||||
|
||||
func (c *PKIIssueCACommand) Synopsis() string {
|
||||
return "Given a Parent Certificate, and a List of Generation Parameters, Creates an Issue on a Specified Mount"
|
||||
}
|
||||
|
||||
func (c *PKIIssueCACommand) Help() string {
|
||||
helpText := `
|
||||
Usage: vault pki issue PARENT CHILD_MOUNT options
|
||||
|
||||
PARENT is the fully qualified path of the Certificate Authority in vault which will issue the new intermediate certificate.
|
||||
|
||||
CHILD_MOUNT is the path of the mount in vault where the new issuer is saved.
|
||||
|
||||
options are the superset of the options passed to generate/intermediate and sign-intermediate commands. At least one option must be set.
|
||||
|
||||
This command creates a intermediate certificate authority certificate signed by the parent in the CHILD_MOUNT.
|
||||
|
||||
` + c.Flags().Help()
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
|
||||
func (c *PKIIssueCACommand) Flags() *FlagSets {
|
||||
set := c.flagSet(FlagSetHTTP | FlagSetOutputFormat)
|
||||
f := set.NewFlagSet("Command Options")
|
||||
|
||||
f.StringVar(&StringVar{
|
||||
Name: "type",
|
||||
Target: &c.flagKeyStorageSource,
|
||||
Default: "internal",
|
||||
EnvVar: "",
|
||||
Usage: `Options are "existing" - to use an existing key inside vault, "internal" - to generate a new key inside vault, or "kms" - to link to an external key. Exported keys are not available through this API.`,
|
||||
Completion: complete.PredictSet("internal", "existing", "kms"),
|
||||
})
|
||||
|
||||
f.StringVar(&StringVar{
|
||||
Name: "issuer_name",
|
||||
Target: &c.flagNewIssuerName,
|
||||
Default: "",
|
||||
EnvVar: "",
|
||||
Usage: `If present, the newly created issuer will be given this name.`,
|
||||
})
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
func (c *PKIIssueCACommand) Run(args []string) int {
|
||||
// Parse Args
|
||||
f := c.Flags()
|
||||
if err := f.Parse(args); err != nil {
|
||||
c.UI.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
args = f.Args()
|
||||
|
||||
if len(args) < 3 {
|
||||
c.UI.Error("Not enough arguments expected parent issuer and child-mount location and some key_value argument")
|
||||
return 1
|
||||
}
|
||||
|
||||
stdin := (io.Reader)(os.Stdin)
|
||||
data, err := parseArgsData(stdin, args[2:])
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Sprintf("Failed to parse K=V data: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
// Check We Have a Client
|
||||
client, err := c.Client()
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Sprintf("Failed to obtain client: %v", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
// Sanity Check the Parent Issuer
|
||||
parentMountIssuer := sanitizePath(args[0]) // /pki/issuer/default
|
||||
_, parentIssuerName := paths.Split(parentMountIssuer)
|
||||
if !strings.Contains(parentMountIssuer, "/issuer/") {
|
||||
c.UI.Error(fmt.Sprintf("Parent Issuer %v is Not a PKI Issuer Path of the format /mount/issuer/issuer-ref", parentMountIssuer))
|
||||
}
|
||||
_, err = client.Logical().Read(parentMountIssuer + "/json")
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Sprintf("Unable to access parent issuer %v: %v", parentMountIssuer, err))
|
||||
}
|
||||
|
||||
// Set-up Failure State (Immediately Before First Write Call)
|
||||
intermediateMount := sanitizePath(args[1])
|
||||
failureState := inCaseOfFailure{
|
||||
intermediateMount: intermediateMount,
|
||||
parentMount: strings.Split(parentMountIssuer, "/issuer/")[0],
|
||||
parentIssuer: parentMountIssuer,
|
||||
newName: c.flagNewIssuerName,
|
||||
}
|
||||
|
||||
// Generate Certificate Signing Request
|
||||
csrResp, err := client.Logical().Write(intermediateMount+"/intermediate/generate/"+c.flagKeyStorageSource, data)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "no handler for route") { // Mount Given Does Not Exist
|
||||
c.UI.Error(fmt.Sprintf("Given Intermediate Mount %v Does Not Exist: %v", intermediateMount, err))
|
||||
} else if strings.Contains(err.Error(), "unsupported path") { // Expected if Not a PKI Mount
|
||||
c.UI.Error(fmt.Sprintf("Given Intermeidate Mount %v Is Not a PKI Mount: %v", intermediateMount, err))
|
||||
} else {
|
||||
c.UI.Error(fmt.Sprintf("Failled to Generate Intermediate CSR on %v: %v", intermediateMount, err))
|
||||
}
|
||||
return 1
|
||||
}
|
||||
// Parse CSR Response, Also Verifies that this is a PKI Mount
|
||||
// (eg. calling the above call on cubbyhole/ won't return an error response)
|
||||
csrPemRaw, present := csrResp.Data["csr"]
|
||||
if !present {
|
||||
c.UI.Error(fmt.Sprintf("Failed to Generate Intermediate CSR on %v, got response: %v", intermediateMount, csrResp))
|
||||
return 1
|
||||
}
|
||||
keyIdRaw, present := csrResp.Data["key_id"]
|
||||
if !present && c.flagKeyStorageSource == "internal" {
|
||||
c.UI.Error(fmt.Sprintf("Failed to Generate Key on %v, got response: %v", intermediateMount, csrResp))
|
||||
return 1
|
||||
}
|
||||
|
||||
// If that all Parses, then we've successfully generated a CSR! Save It (and the Key-ID)
|
||||
failureState.csrGenerated = true
|
||||
if c.flagKeyStorageSource == "internal" {
|
||||
failureState.createdKeyId = keyIdRaw.(string)
|
||||
}
|
||||
csr := csrPemRaw.(string)
|
||||
failureState.csr = csr
|
||||
data["csr"] = csr
|
||||
|
||||
// Next, Sign the CSR
|
||||
rootResp, err := client.Logical().Write(parentMountIssuer+"/sign-intermediate", data)
|
||||
if err != nil {
|
||||
c.UI.Error(failureState.generateFailureMessage())
|
||||
c.UI.Error(fmt.Sprintf("Error Signing Intermiate On %v", err))
|
||||
return 1
|
||||
}
|
||||
// Success! Save Our Progress (and Parse the Response)
|
||||
failureState.csrSigned = true
|
||||
serialNumber := rootResp.Data["serial_number"].(string)
|
||||
failureState.certSerialNumber = serialNumber
|
||||
|
||||
caChain := rootResp.Data["ca_chain"].([]interface{})
|
||||
caChainPemBundle := ""
|
||||
for _, cert := range caChain {
|
||||
caChainPemBundle += cert.(string) + "\n"
|
||||
}
|
||||
failureState.caChain = caChainPemBundle
|
||||
|
||||
// Next Import Certificate
|
||||
certificate := rootResp.Data["certificate"].(string)
|
||||
issuerId, err := importIssuerWithName(client, intermediateMount, certificate, c.flagNewIssuerName)
|
||||
failureState.certIssuerId = issuerId
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "error naming issuer") {
|
||||
failureState.certImported = true
|
||||
c.UI.Error(failureState.generateFailureMessage())
|
||||
c.UI.Error(fmt.Sprintf("Error Naming Newly Imported Issuer: %v", err))
|
||||
return 1
|
||||
} else {
|
||||
c.UI.Error(failureState.generateFailureMessage())
|
||||
c.UI.Error(fmt.Sprintf("Error Importing Into %v Newly Created Issuer %v: %v", intermediateMount, certificate, err))
|
||||
return 1
|
||||
}
|
||||
}
|
||||
failureState.certImported = true
|
||||
|
||||
// Then Import Issuing Certificate
|
||||
issuingCa := rootResp.Data["issuing_ca"].(string)
|
||||
_, err = importIssuerWithName(client, intermediateMount, issuingCa, parentIssuerName)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "error naming issuer") {
|
||||
c.UI.Warn(fmt.Sprintf("Unable to Set Name on Parent Cert from %v Imported Into %v with serial %v, err: %v", parentIssuerName, intermediateMount, serialNumber, err))
|
||||
} else {
|
||||
c.UI.Error(failureState.generateFailureMessage())
|
||||
c.UI.Error(fmt.Sprintf("Error Importing Into %v Newly Created Issuer %v: %v", intermediateMount, certificate, err))
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
// Finally Import CA_Chain (just in case there's more information)
|
||||
if len(caChain) > 2 { // We've already imported parent cert and newly issued cert above
|
||||
importData := map[string]interface{}{
|
||||
"pem_bundle": caChainPemBundle,
|
||||
}
|
||||
_, err := client.Logical().Write(intermediateMount+"/issuers/import/cert", importData)
|
||||
if err != nil {
|
||||
c.UI.Error(failureState.generateFailureMessage())
|
||||
c.UI.Error(fmt.Sprintf("Error Importing CaChain into %v: %v", intermediateMount, err))
|
||||
return 1
|
||||
}
|
||||
}
|
||||
failureState.caChainImported = true
|
||||
|
||||
// Finally we read our newly issued certificate in order to tell our caller about it
|
||||
c.readAndOutputNewCertificate(client, intermediateMount, issuerId)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *PKIIssueCACommand) readAndOutputNewCertificate(client *api.Client, intermediateMount string, issuerId string) {
|
||||
resp, err := client.Logical().Read(sanitizePath(intermediateMount + "/issuer/" + issuerId))
|
||||
if err != nil || resp == nil {
|
||||
c.UI.Error(fmt.Sprintf("Error Reading Fully Imported Certificate from %v : %v",
|
||||
intermediateMount+"/issuer/"+issuerId, err))
|
||||
}
|
||||
|
||||
OutputSecret(c.UI, resp)
|
||||
}
|
||||
|
||||
func importIssuerWithName(client *api.Client, mount string, bundle string, name string) (issuerUUID string, err error) {
|
||||
importData := map[string]interface{}{
|
||||
"pem_bundle": bundle,
|
||||
}
|
||||
writeResp, err := client.Logical().Write(mount+"/issuers/import/cert", importData)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
mapping := writeResp.Data["mapping"].(map[string]interface{})
|
||||
if len(mapping) > 1 {
|
||||
return "", fmt.Errorf("multiple issuers returned, while expected one, got %v", writeResp)
|
||||
}
|
||||
for issuerId := range mapping {
|
||||
issuerUUID = issuerId
|
||||
}
|
||||
if name != "" && name != "default" {
|
||||
nameReq := map[string]interface{}{
|
||||
"issuer_name": name,
|
||||
}
|
||||
ctx := context.Background()
|
||||
_, err = client.Logical().JSONMergePatch(ctx, mount+"/issuer/"+issuerUUID, nameReq)
|
||||
if err != nil {
|
||||
return issuerUUID, fmt.Errorf("error naming issuer %v to %v: %v", issuerUUID, name, err)
|
||||
}
|
||||
}
|
||||
return issuerUUID, nil
|
||||
}
|
||||
|
||||
type inCaseOfFailure struct {
|
||||
csrGenerated bool
|
||||
csrSigned bool
|
||||
certImported bool
|
||||
certNamed bool
|
||||
caChainImported bool
|
||||
|
||||
intermediateMount string
|
||||
createdKeyId string
|
||||
csr string
|
||||
caChain string
|
||||
parentMount string
|
||||
parentIssuer string
|
||||
certSerialNumber string
|
||||
certIssuerId string
|
||||
newName string
|
||||
}
|
||||
|
||||
func (state inCaseOfFailure) generateFailureMessage() string {
|
||||
message := "A failure has occurred"
|
||||
|
||||
if state.csrGenerated {
|
||||
message += fmt.Sprintf(" after \n a Certificate Signing Request was successfully generated on mount %v", state.intermediateMount)
|
||||
}
|
||||
if state.csrSigned {
|
||||
message += fmt.Sprintf(" and after \n that Certificate Signing Request was successfully signed by mount %v", state.parentMount)
|
||||
}
|
||||
if state.certImported {
|
||||
message += fmt.Sprintf(" and after \n the signed certificate was reimported into mount %v , with issuerID %v", state.intermediateMount, state.certIssuerId)
|
||||
}
|
||||
|
||||
if state.csrGenerated {
|
||||
message += "\n\nTO CONTINUE: \n" + state.toContinue()
|
||||
}
|
||||
if state.csrGenerated && !state.certImported {
|
||||
message += "\n\nTO ABORT: \n" + state.toAbort()
|
||||
}
|
||||
|
||||
message += "\n"
|
||||
|
||||
return message
|
||||
}
|
||||
|
||||
func (state inCaseOfFailure) toContinue() string {
|
||||
message := ""
|
||||
if !state.csrSigned {
|
||||
message += fmt.Sprintf("You can continue to work with this Certificate Signing Request CSR PEM, by saving"+
|
||||
" it as `pki_int.csr`: %v \n Then call `vault write %v/sign-intermediate csr=@pki_int.csr ...` adding the "+
|
||||
"same key-value arguements as to `pki issue` (except key_type and issuer_name) to generate the certificate "+
|
||||
"and ca_chain", state.csr, state.parentIssuer)
|
||||
}
|
||||
if !state.certImported {
|
||||
if state.caChain != "" {
|
||||
message += fmt.Sprintf("The certificate chain, signed by %v, for this new certificate is: %v", state.parentIssuer, state.caChain)
|
||||
}
|
||||
message += fmt.Sprintf("You can continue to work with this Certificate (and chain) by saving it as "+
|
||||
"chain.pem and importing it as `vault write %v/issuers/import/cert pem_bundle=@chain.pem`",
|
||||
state.intermediateMount)
|
||||
}
|
||||
if !state.certNamed {
|
||||
issuerId := state.certIssuerId
|
||||
if issuerId == "" {
|
||||
message += fmt.Sprintf("The issuer_id is returned as the key in a key_value map from importing the " +
|
||||
"certificate chain.")
|
||||
issuerId = "<issuer-uuid>"
|
||||
}
|
||||
message += fmt.Sprintf("You can name the newly imported issuer by calling `vault patch %v/issuer/%v "+
|
||||
"issuer_name=%v`", state.intermediateMount, issuerId, state.newName)
|
||||
}
|
||||
return message
|
||||
}
|
||||
|
||||
func (state inCaseOfFailure) toAbort() string {
|
||||
if !state.csrGenerated || (!state.csrSigned && state.createdKeyId == "") {
|
||||
return "No state was created by running this command. Try rerunning this command after resolving the error."
|
||||
}
|
||||
message := ""
|
||||
if state.csrGenerated && state.createdKeyId != "" {
|
||||
message += fmt.Sprintf(" A key, with key ID %v was created on mount %v as part of this command."+
|
||||
" If you do not with to use this key and corresponding CSR/cert, you can delete that information by calling"+
|
||||
" `vault delete %v/key/%v`", state.createdKeyId, state.intermediateMount, state.intermediateMount, state.createdKeyId)
|
||||
}
|
||||
if state.csrSigned {
|
||||
message += fmt.Sprintf("A certificate with serial number %v was signed by mount %v as part of this command."+
|
||||
" If you do not want to use this certificate, consider revoking it by calling `vault write %v/revoke/%v`",
|
||||
state.certSerialNumber, state.parentMount, state.parentMount, state.certSerialNumber)
|
||||
}
|
||||
//if state.certImported {
|
||||
// message += fmt.Sprintf("An issuer with UUID %v was created on mount %v as part of this command. " +
|
||||
// "If you do not wish to use this issuer, consider deleting it by calling `vault delete %v/issuer/%v`",
|
||||
// state.certIssuerId, state.intermediateMount, state.intermediateMount, state.certIssuerId)
|
||||
//}
|
||||
|
||||
return message
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/vault/api"
|
||||
)
|
||||
|
||||
func TestPKIIssueIntermediate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client, closer := testVaultServer(t)
|
||||
defer closer()
|
||||
|
||||
// Relationship Map to Create
|
||||
// pki-root | pki-newroot | pki-empty
|
||||
// RootX1 RootX2 RootX4 RootX3
|
||||
// | |
|
||||
// ----------------------------------------------
|
||||
// v v
|
||||
// IntX1 IntX2 pki-int
|
||||
// | |
|
||||
// v v
|
||||
// IntX3 (-----------------------) IntX3
|
||||
//
|
||||
// Here X1,X2 have the same name (same mount)
|
||||
// RootX4 uses the same key as RootX1 (but a different common_name/subject)
|
||||
// RootX3 has the same name, and is on a different mount
|
||||
// RootX1 has issued IntX1; RootX3 has issued IntX2
|
||||
createComplicatedIssuerSetUpWithIssueIntermediate(t, client)
|
||||
|
||||
runPkiVerifySignTests(t, client)
|
||||
|
||||
runPkiListIntermediateTests(t, client)
|
||||
}
|
||||
|
||||
func createComplicatedIssuerSetUpWithIssueIntermediate(t *testing.T, client *api.Client) {
|
||||
// Relationship Map to Create
|
||||
// pki-root | pki-newroot | pki-empty
|
||||
// RootX1 RootX2 RootX4 RootX3
|
||||
// | |
|
||||
// ----------------------------------------------
|
||||
// v v
|
||||
// IntX1 IntX2 pki-int
|
||||
// | |
|
||||
// v v
|
||||
// IntX3 (-----------------------) IntX3
|
||||
//
|
||||
// Here X1,X2 have the same name (same mount)
|
||||
// RootX4 uses the same key as RootX1 (but a different common_name/subject)
|
||||
// RootX3 has the same name, and is on a different mount
|
||||
// RootX1 has issued IntX1; RootX3 has issued IntX2
|
||||
|
||||
if err := client.Sys().Mount("pki-root", &api.MountInput{
|
||||
Type: "pki",
|
||||
Config: api.MountConfigInput{
|
||||
MaxLeaseTTL: "36500d",
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatalf("pki mount error: %#v", err)
|
||||
}
|
||||
|
||||
if err := client.Sys().Mount("pki-newroot", &api.MountInput{
|
||||
Type: "pki",
|
||||
Config: api.MountConfigInput{
|
||||
MaxLeaseTTL: "36500d",
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatalf("pki mount error: %#v", err)
|
||||
}
|
||||
|
||||
if err := client.Sys().Mount("pki-int", &api.MountInput{
|
||||
Type: "pki",
|
||||
Config: api.MountConfigInput{
|
||||
MaxLeaseTTL: "36500d",
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatalf("pki mount error: %#v", err)
|
||||
}
|
||||
|
||||
// Used to check handling empty list responses: Not Used for Any Issuers / Certificates
|
||||
if err := client.Sys().Mount("pki-empty", &api.MountInput{
|
||||
Type: "pki",
|
||||
Config: api.MountConfigInput{},
|
||||
}); err != nil {
|
||||
t.Fatalf("pki mount error: %#v", err)
|
||||
}
|
||||
|
||||
resp, err := client.Logical().Write("pki-root/root/generate/internal", map[string]interface{}{
|
||||
"key_type": "ec",
|
||||
"common_name": "Root X",
|
||||
"ttl": "3650d",
|
||||
"issuer_name": "rootX1",
|
||||
"key_name": "rootX1",
|
||||
})
|
||||
if err != nil || resp == nil {
|
||||
t.Fatalf("failed to prime CA: %v", err)
|
||||
}
|
||||
|
||||
resp, err = client.Logical().Write("pki-root/root/generate/internal", map[string]interface{}{
|
||||
"key_type": "ec",
|
||||
"common_name": "Root X",
|
||||
"ttl": "3650d",
|
||||
"issuer_name": "rootX2",
|
||||
})
|
||||
if err != nil || resp == nil {
|
||||
t.Fatalf("failed to prime CA: %v", err)
|
||||
}
|
||||
|
||||
if resp, err := client.Logical().Write("pki-newroot/root/generate/internal", map[string]interface{}{
|
||||
"key_type": "ec",
|
||||
"common_name": "Root X",
|
||||
"ttl": "3650d",
|
||||
"issuer_name": "rootX3",
|
||||
}); err != nil || resp == nil {
|
||||
t.Fatalf("failed to prime CA: %v", err)
|
||||
}
|
||||
|
||||
if resp, err := client.Logical().Write("pki-root/root/generate/existing", map[string]interface{}{
|
||||
"common_name": "Root X4",
|
||||
"ttl": "3650d",
|
||||
"issuer_name": "rootX4",
|
||||
"key_ref": "rootX1",
|
||||
}); err != nil || resp == nil {
|
||||
t.Fatalf("failed to prime CA: %v", err)
|
||||
}
|
||||
|
||||
// Next we create the Intermediates Using the Issue Intermediate Command
|
||||
stdout := bytes.NewBuffer(nil)
|
||||
stderr := bytes.NewBuffer(nil)
|
||||
runOpts := &RunOptions{
|
||||
Stdout: stdout,
|
||||
Stderr: stderr,
|
||||
Client: client,
|
||||
}
|
||||
|
||||
// Intermediate X1
|
||||
intX1CallArgs := []string{
|
||||
"pki", "issue", "-format=json", "-issuer_name=intX1",
|
||||
"pki-root/issuer/rootX1",
|
||||
"pki-int/",
|
||||
"key_type=rsa",
|
||||
"common_name=Int X1",
|
||||
"ttl=3650d",
|
||||
}
|
||||
codeOut := RunCustom(intX1CallArgs, runOpts)
|
||||
if codeOut != 0 {
|
||||
t.Fatalf("error issuing intermediate X1, code: %d \n stdout: %v \n stderr: %v", codeOut, stdout, stderr)
|
||||
}
|
||||
|
||||
// Intermediate X2
|
||||
intX2CallArgs := []string{
|
||||
"pki", "issue", "-format=json", "-issuer_name=intX2",
|
||||
"pki-newroot/issuer/rootX3",
|
||||
"pki-int/",
|
||||
"key_type=ed25519",
|
||||
"common_name=Int X2",
|
||||
"ttl=3650d",
|
||||
}
|
||||
codeOut = RunCustom(intX2CallArgs, runOpts)
|
||||
if codeOut != 0 {
|
||||
t.Fatalf("error issuing intermediate X2, code: %d \n stdout: %v \n stderr: %v", codeOut, stdout, stderr)
|
||||
}
|
||||
|
||||
// Intermediate X3
|
||||
// Clear Buffers so that we can unmarshall json of just this call
|
||||
stdout = bytes.NewBuffer(nil)
|
||||
stderr = bytes.NewBuffer(nil)
|
||||
runOpts = &RunOptions{
|
||||
Stdout: stdout,
|
||||
Stderr: stderr,
|
||||
Client: client,
|
||||
}
|
||||
intX3OriginalCallArgs := []string{
|
||||
"pki", "issue", "-format=json", "-issuer_name=intX3",
|
||||
"pki-int/issuer/intX1",
|
||||
"pki-int/",
|
||||
"key_type=rsa",
|
||||
"common_name=Int X3",
|
||||
"ttl=3650d",
|
||||
}
|
||||
codeOut = RunCustom(intX3OriginalCallArgs, runOpts)
|
||||
if codeOut != 0 {
|
||||
t.Fatalf("error issuing intermediate X3, code: %d \n stdout: %v \n stderr: %v", codeOut, stdout, stderr)
|
||||
}
|
||||
var intX3Resp map[string]interface{}
|
||||
json.Unmarshal(stdout.Bytes(), &intX3Resp)
|
||||
intX3Data := intX3Resp["data"].(map[string]interface{})
|
||||
keyId := intX3Data["key_id"].(string)
|
||||
|
||||
intX3AdaptedCallArgs := []string{
|
||||
"pki", "issue", "-format=json", "-issuer_name=intX3also", "-type=existing",
|
||||
"pki-int/issuer/intX2",
|
||||
"pki-int/",
|
||||
"key_ref=" + keyId,
|
||||
"common_name=Int X3",
|
||||
"ttl=3650d",
|
||||
}
|
||||
codeOut = RunCustom(intX3AdaptedCallArgs, runOpts)
|
||||
if codeOut != 0 {
|
||||
t.Fatalf("error issuing intermediate X3also, code: %d \n stdout: %v \n stderr: %v", codeOut, stdout, stderr)
|
||||
}
|
||||
}
|
|
@ -3,6 +3,8 @@ package command
|
|||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/vault/api"
|
||||
)
|
||||
|
||||
func TestPKIListIntermediate(t *testing.T) {
|
||||
|
@ -28,6 +30,10 @@ func TestPKIListIntermediate(t *testing.T) {
|
|||
// RootX1 has issued IntX1; RootX3 has issued IntX2
|
||||
createComplicatedIssuerSetUp(t, client)
|
||||
|
||||
runPkiListIntermediateTests(t, client)
|
||||
}
|
||||
|
||||
func runPkiListIntermediateTests(t *testing.T, client *api.Client) {
|
||||
cases := []struct {
|
||||
name string
|
||||
args []string
|
||||
|
|
|
@ -18,7 +18,7 @@ func TestPKIVerifySign(t *testing.T) {
|
|||
defer closer()
|
||||
|
||||
// Relationship Map to Create
|
||||
// pki-root | pki-newroot
|
||||
// pki-root | pki-newroot | pki-empty
|
||||
// RootX1 RootX2 RootX4 RootX3
|
||||
// | |
|
||||
// ----------------------------------------------
|
||||
|
@ -34,6 +34,10 @@ func TestPKIVerifySign(t *testing.T) {
|
|||
// RootX1 has issued IntX1; RootX3 has issued IntX2
|
||||
createComplicatedIssuerSetUp(t, client)
|
||||
|
||||
runPkiVerifySignTests(t, client)
|
||||
}
|
||||
|
||||
func runPkiVerifySignTests(t *testing.T, client *api.Client) {
|
||||
cases := []struct {
|
||||
name string
|
||||
args []string
|
||||
|
@ -217,7 +221,6 @@ func createComplicatedIssuerSetUp(t *testing.T, client *api.Client) {
|
|||
"issuer_name": "rootX1",
|
||||
"key_name": "rootX1",
|
||||
})
|
||||
|
||||
if err != nil || resp == nil {
|
||||
t.Fatalf("failed to prime CA: %v", err)
|
||||
}
|
||||
|
@ -228,7 +231,6 @@ func createComplicatedIssuerSetUp(t *testing.T, client *api.Client) {
|
|||
"ttl": "3650d",
|
||||
"issuer_name": "rootX2",
|
||||
})
|
||||
|
||||
if err != nil || resp == nil {
|
||||
t.Fatalf("failed to prime CA: %v", err)
|
||||
}
|
||||
|
@ -414,6 +416,7 @@ func createComplicatedIssuerSetUp(t *testing.T, client *api.Client) {
|
|||
break
|
||||
}
|
||||
}
|
||||
|
||||
// sign by intX2 and import
|
||||
int3CertResp2, err := client.Logical().Write("pki-int/issuer/intX2/sign-intermediate", map[string]interface{}{
|
||||
"csr": int3Csr,
|
||||
|
|
|
@ -122,14 +122,14 @@ func (l *InmemLayer) Dial(addr string, timeout time.Duration, tlsConfig *tls.Con
|
|||
panic(fmt.Sprintf("%q attempted to dial itself", l.addr))
|
||||
}
|
||||
|
||||
// This simulates an i/o timeout by sleeping for 20 seconds and returning
|
||||
// an error when the forceTimeout name is the same as the host we are
|
||||
// currently connecting to. Useful for checking how gRPC connections react
|
||||
// This simulates an i/o timeout by sleeping for 20 seconds and returning
|
||||
// an error when the forceTimeout name is the same as the host we are
|
||||
// currently connecting to. Useful for checking how gRPC connections react
|
||||
// with timeouts.
|
||||
if l.forceTimeout == addr {
|
||||
l.logger.Debug("forcing timeout", "addr", addr, "me", l.addr)
|
||||
|
||||
// gRPC sets a deadline of 20 seconds on the dail attempt, so
|
||||
|
||||
// gRPC sets a deadline of 20 seconds on the dail attempt, so
|
||||
// matching that here.
|
||||
time.Sleep(time.Second * 20)
|
||||
return nil, deadlineError("i/o timeout")
|
||||
|
|
Loading…
Reference in New Issue