Skip to content

Commit

Permalink
Merge pull request #484 from Venafi/VC-32826-provision-for-vcert-cli
Browse files Browse the repository at this point in the history
[VCert CLI] Support for Certificate Provision and more changes
  • Loading branch information
luispresuelVenafi authored May 24, 2024
2 parents a03a6d9 + 990f5fc commit 8c021a6
Show file tree
Hide file tree
Showing 15 changed files with 543 additions and 131 deletions.
43 changes: 28 additions & 15 deletions cmd/vcert/args.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,29 @@ import (
)

const (
commandGenCSRName = "gencsr"
commandEnrollName = "enroll"
commandPickupName = "pickup"
commandRevokeName = "revoke"
commandRenewName = "renew"
commandRetireName = "retire"
commandGetCredName = "getcred"
commandCheckCredName = "checkcred"
commandVoidCredName = "voidcred"
commandCreatePolicyName = "setpolicy"
commandGetePolicyName = "getpolicy"
commandSshPickupName = "sshpickup"
commandSshEnrollName = "sshenroll"
commandSshGetConfigName = "sshgetconfig"
commandGenCSRName = "gencsr"
commandEnrollName = "enroll"
commandPickupName = "pickup"
commandRevokeName = "revoke"
commandRenewName = "renew"
commandRetireName = "retire"
commandGetCredName = "getcred"
commandCheckCredName = "checkcred"
commandVoidCredName = "voidcred"
commandCreatePolicyName = "setpolicy"
commandGetePolicyName = "getpolicy"
commandSshPickupName = "sshpickup"
commandSshEnrollName = "sshenroll"
commandSshGetConfigName = "sshgetconfig"
commandProvisionName = "provision"
subCommandCloudKeystoreName = "cloudkeystore"
)

var (
flags commandFlags
flags commandFlags
provisionCommands = stringSlice{
subCommandCloudKeystoreName,
}
)

type commandFlags struct {
Expand Down Expand Up @@ -141,4 +146,12 @@ type commandFlags struct {
sshCertWindows bool
sshFileCertEnroll string
sshFileGetConfig string
certificateID string
keystoreID string
providerName string
keystoreName string
keystoreCertName string
provisionOutputFile string
provisionPickupID string
provisionFormat string
}
100 changes: 100 additions & 0 deletions cmd/vcert/cmdCloudKeystores.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package main

import (
"fmt"
"log"
"os"
"strings"

"github.com/urfave/cli/v2"

"github.com/Venafi/vcert/v5"
"github.com/Venafi/vcert/v5/pkg/endpoint"
"github.com/Venafi/vcert/v5/pkg/venafi/cloud"
)

var (
subCommandCloudKeystore = &cli.Command{
Name: subCommandCloudKeystoreName,
Flags: provisionFlags,
Usage: "provision certificate from Venafi Platform to Cloud Keystore",
UsageText: `vcert provision cloudkeystore <Required Venafi Control Plane> <Options>
vcert provision cloudkeystore --platform vcp -k <VCP API key> --certificate-id xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx --keystore-id xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx --format json
vcert provision cloudkeystore --platform vcp -k <VCP API key> --pickup-id xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx --provider-name "My GCP Provider"--keystore-name "My GCP provider" --certificate-name "example-venafi-com"
vcert provision cloudkeystore -p vcp -t <VCP access token> --certificate-id xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx --provider-name "My GCP Provider" --keystore-name "My GCP provider" --file "/path/to/file.txt"`,
Action: doCommandProvisionCloudKeystore,
}
)

func doCommandProvisionCloudKeystore(c *cli.Context) error {
err := validateProvisionFlags(c.Command.Name)
if err != nil {
return err
}
err = setTLSConfig()
if err != nil {
return err
}

cfg, err := buildConfig(c, &flags)
if err != nil {
return fmt.Errorf("failed to build vcert config: %s", err)
}

connector, err := vcert.NewClient(&cfg)
if err != nil {
logf("Unable to connect to %s: %s", cfg.ConnectorType, err)
} else {
logf("Successfully connected to %s", cfg.ConnectorType)
}

var req = &endpoint.ProvisioningRequest{}
var options *endpoint.ProvisioningOptions

log.Printf("fetching keystore information for provided keystore information from flags. KeystoreID: %s, KeystoreName: %s, ProviderName: %s", flags.keystoreID, flags.keystoreName, flags.providerName)
getKeystoreReq := buildGetCloudKeystoreRequest(&flags)
cloudKeystore, err := connector.(*cloud.Connector).GetCloudKeystore(getKeystoreReq)
if err != nil {
return err
}
log.Printf("successfully fetched keystore")

if flags.pickupIDFile != "" {
bytes, err := os.ReadFile(flags.pickupIDFile)
if err != nil {
return fmt.Errorf("failed to read Pickup ID value: %s", err)
}
flags.pickupID = strings.TrimSpace(string(bytes))
}

req, options = fillProvisioningRequest(req, *cloudKeystore, &flags)

metadata, err := connector.ProvisionCertificate(req, options)
if err != nil {
return err
}

result := ProvisioningResult{}
switch cloudKeystore.Type {
case cloud.KeystoreTypeACM:
result.ARN = metadata.GetAWSCertificateMetadata().GetARN()
case cloud.KeystoreTypeAKV:
result.AzureID = metadata.GetAzureCertificateMetadata().GetID()
result.AzureName = metadata.GetAzureCertificateMetadata().GetName()
result.AzureVersion = metadata.GetAzureCertificateMetadata().GetVersion()
case cloud.KeystoreTypeGCM:
result.GcpID = metadata.GetGCPCertificateMetadata().GetID()
result.GcpName = metadata.GetGCPCertificateMetadata().GetName()
}

result.MachineIdentityId = metadata.GetMachineIdentityMetadata().GetID()
result.MachineIdentityActionType = metadata.GetMachineIdentityMetadata().GetActionType()

err = result.Flush(flags.provisionFormat)

if err != nil {
return fmt.Errorf("failed to output the results: %s", err)
}
return nil
}
32 changes: 32 additions & 0 deletions cmd/vcert/cmdProvisioning.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package main

import (
"fmt"
"strings"

"github.com/urfave/cli/v2"
)

var (
commandProvision = &cli.Command{
Before: runBeforeCommand,
Action: doCommandProvision,
Name: commandProvisionName,
Usage: "To provision a certificate from Venafi Platform to a Cloud Keystore",
Subcommands: []*cli.Command{subCommandCloudKeystore},
}
)

func doCommandProvision(c *cli.Context) error {
return fmt.Errorf("the following subcommand(s) are required: \n%s", createBulletList(provisionCommands))
}

func createBulletList(items []string) string {
var builder strings.Builder
for _, item := range items {
builder.WriteString("• ")
builder.WriteString(item)
builder.WriteString("\n")
}
return builder.String()
}
64 changes: 64 additions & 0 deletions cmd/vcert/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,56 @@ var (
TakesFile: true,
}

flagCertificateID = &cli.StringFlag{
Name: "certificate-id",
Usage: "The id of the certificate to be provisioned to a cloud keystore.",
Destination: &flags.certificateID,
}

flagKeystoreID = &cli.StringFlag{
Name: "keystore-id",
Usage: "The id of the cloud keystore where the certificate will be provisioned.",
Destination: &flags.keystoreID,
}

flagKeystoreName = &cli.StringFlag{
Name: "keystore-name",
Usage: "The name of the cloud keystore where the certificate will be provisioned. Must be set along with provider-name flag.",
Destination: &flags.keystoreName,
}

flagProviderName = &cli.StringFlag{
Name: "provider-name",
Usage: "Name of the cloud provider which owns the cloud keystore where the certificate will be provisioned. Must be set along with keystore-name flag.",
Destination: &flags.providerName,
}

flagKeystoreCertName = &cli.StringFlag{
Name: "certificate-name",
Usage: "Use to specify Cloud Keystore Certificate Name if it supports it",
Destination: &flags.keystoreCertName,
}

flagProvisionOutputFile = &cli.StringFlag{
Name: "file",
Usage: "Use to specify a file name and a location where the output should be written. " +
"Example: --file /path-to/provision-output",
Destination: &flags.provisionOutputFile,
TakesFile: true,
}

flagProvisionPickupID = &cli.StringFlag{
Name: "pickup-id",
Usage: "Use to specify the Pickup ID (for VCP is the Request ID) of the certificate to be provisioned.",
Destination: &flags.provisionPickupID,
}

flagProvisionFormat = &cli.StringFlag{
Name: "format",
Usage: "The format of the operation output: text or JSON. Defaults to text.",
Destination: &flags.provisionFormat,
}

commonFlags = []cli.Flag{flagInsecure, flagVerbose, flagNoPrompt}
keyFlags = []cli.Flag{flagKeyType, flagKeySize, flagKeyCurve, flagKeyFile, flagKeyPassword}
sansFlags = []cli.Flag{flagDNSSans, flagEmailSans, flagIPSans, flagURISans, flagUPNSans}
Expand Down Expand Up @@ -846,6 +896,20 @@ var (
)),
)

provisionFlags = flagsApppend(
credentialsFlags,
flagPlatform,
flagCertificateID,
flagProvisionPickupID,
flagPickupIDFile,
flagKeystoreCertName,
flagProviderName,
flagKeystoreName,
flagKeystoreID,
flagProvisionFormat,
flagProvisionOutputFile, // TODO: implement this flag
)

commonCredFlags = []cli.Flag{flagConfig, flagProfile, flagUrl, flagToken, flagTrustBundle}

getCredFlags = sortedFlags(flagsApppend(
Expand Down
2 changes: 2 additions & 0 deletions cmd/vcert/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ func main() {
commandSshEnroll,
commandSshGetConfig,
commandRunPlaybook,
commandProvision,
},
EnableBashCompletion: true, //todo: write BashComplete function for options
Authors: authors,
Expand Down Expand Up @@ -115,6 +116,7 @@ ACTIONS:
retire tpp | vcp To retire a certificate
revoke tpp To revoke a certificate
run tpp | vcp | firefly To retrieve and install certificates using a vcert playbook file
provision vcp To provision a certificate to cloud keystore
getpolicy tpp | vcp To retrieve the certificate policy of a zone
setpolicy tpp | vcp To apply a certificate policy specification to a zone
Expand Down
Loading

0 comments on commit 8c021a6

Please sign in to comment.