github.com/hashicorp/vault/sdk@v0.11.0/helper/certutil/cieps.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package certutil 5 6 import ( 7 "crypto/x509" 8 "encoding/pem" 9 "fmt" 10 ) 11 12 // Source of the issuance request: sign implies that the key material was 13 // generated by the user and submitted via a CSR request but only ACL level 14 // validation was applied; issue implies that Vault created the key material 15 // on behalf of the user with ACL level validation occurring; ACME implies 16 // that the user submitted a CSR and that additional ACME validation has 17 // occurred before sending the request to the external service for 18 // construction. 19 type CIEPSIssuanceMode string 20 21 const ( 22 SignCIEPSMode = "sign" 23 IssueCIEPSMode = "issue" 24 ACMECIEPSMode = "acme" 25 ICACIEPSMode = "ica" 26 ) 27 28 // Configuration of the issuer and mount at the time of this request; 29 // states the issuer's templated AIA information (falling back to the 30 // mount-global config if no per-issuer AIA info is set, the issuer's 31 // leaf_not_after_behavior (permit/truncate/err) for TTLs exceeding the 32 // issuer's validity period, and the mount's default and max TTL. 33 type CIEPSIssuanceConfig struct { 34 AIAValues *URLEntries `json:"aia_values"` 35 LeafNotAfterBehavior string `json:"leaf_not_after_behavior"` 36 MountDefaultTTL string `json:"mount_default_ttl"` 37 MountMaxTTL string `json:"mount_max_ttl"` 38 } 39 40 // Structured parameters sent by Vault or explicitly validated by Vault 41 // prior to sending. 42 type CIEPSVaultParams struct { 43 PolicyName string `json:"policy_name,omitempty"` 44 Mount string `json:"mount"` 45 Namespace string `json:"ns"` 46 47 // These indicate the type of the cluster node talking to the CIEPS 48 // service. When IsPerfStandby=true, setting StoreCert=true in the 49 // response will result in Vault forwarding the client's request 50 // up to the Performance Secondary's active node and re-trying the 51 // operation (including re-submitting the request to the CIEPS 52 // service). 53 // 54 // Any response returned by the CIEPS service in this case will be 55 // ignored and not signed by the CA's keys. 56 // 57 // IsPRSecondary is set to false when a local mount is used on a 58 // PR Secondary; in this scenario, PR Secondary nodes behave like 59 // PR Primary nodes. From a CIEPS service perspective, no behavior 60 // difference is expected between PR Primary and PR Secondary nodes; 61 // both will issue and store certificates on their active nodes. 62 // This information is included for audit tracking purposes. 63 IsPerfStandby bool `json:"vault_is_performance_standby"` 64 IsPRSecondary bool `json:"vault_is_performance_secondary"` 65 66 IssuanceMode CIEPSIssuanceMode `json:"issuance_mode"` 67 68 GeneratedKey bool `json:"vault_generated_private_key"` 69 70 IssuerName string `json:"requested_issuer_name"` 71 IssuerID string `json:"requested_issuer_id"` 72 IssuerCert string `json:"requested_issuer_cert"` 73 74 Config CIEPSIssuanceConfig `json:"requested_issuance_config"` 75 } 76 77 // Outer request object sent by Vault to the external CIEPS service. 78 // 79 // The top-level fields denote properties about the CIEPS request, 80 // with various request fields containing untrusted and trusted input 81 // respectively. 82 type CIEPSRequest struct { 83 Version int `json:"request_version"` 84 UUID string `json:"request_uuid"` 85 Sync bool `json:"synchronous"` 86 87 UserRequestKV map[string]interface{} `json:"user_request_key_values"` 88 IdentityRequestKV map[string]interface{} `json:"identity_request_key_values,omitempty"` 89 ACMERequestKV map[string]interface{} `json:"acme_request_key_values,omitempty"` 90 VaultRequestKV CIEPSVaultParams `json:"vault_request_values"` 91 92 // Vault guarantees that UserRequestKV will contain a csr parameter 93 // for all request types; this field is useful for engine implementations 94 // to have in parsed format. We assume that this is sent in PEM format, 95 // aligning with other Vault requests. 96 ParsedCSR *x509.CertificateRequest `json:"-"` 97 } 98 99 func (req *CIEPSRequest) ParseUserCSR() error { 100 csrValueRaw, present := req.UserRequestKV["csr"] 101 if !present { 102 return fmt.Errorf("missing expected 'csr' attribute on the request") 103 } 104 105 csrValue, ok := csrValueRaw.(string) 106 if !ok { 107 return fmt.Errorf("unexpected type of 'csr' attribute: %T", csrValueRaw) 108 } 109 110 if csrValue == "" { 111 return fmt.Errorf("unexpectedly empty 'csr' attribute on the request") 112 } 113 114 block, rest := pem.Decode([]byte(csrValue)) 115 if len(rest) > 0 { 116 return fmt.Errorf("failed to decode 'csr': %v bytes of trailing data after PEM block", len(rest)) 117 } 118 if block == nil { 119 return fmt.Errorf("failed to decode 'csr' PEM block") 120 } 121 122 csr, err := x509.ParseCertificateRequest(block.Bytes) 123 if err != nil { 124 return fmt.Errorf("failed to parse certificate request: %w", err) 125 } 126 127 req.ParsedCSR = csr 128 return nil 129 } 130 131 // Expected response object from the external CIEPS service. 132 // 133 // When parsing, Vault will disallow unknown fields, failing the 134 // parse if unknown fields are sent. 135 type CIEPSResponse struct { 136 UUID string `json:"request_uuid"` 137 Error string `json:"error,omitempty"` 138 Warnings []string `json:"warnings,omitempty"` 139 Certificate string `json:"certificate"` 140 ParsedCertificate *x509.Certificate `json:"-"` 141 IssuerRef string `json:"issuer_ref"` 142 StoreCert bool `json:"store_certificate"` 143 GenerateLease bool `json:"generate_lease"` 144 } 145 146 func (c *CIEPSResponse) MarshalCertificate() error { 147 if c.ParsedCertificate == nil || len(c.ParsedCertificate.Raw) == 0 { 148 return fmt.Errorf("no certificate present") 149 } 150 151 pem := pem.EncodeToMemory(&pem.Block{ 152 Type: "CERTIFICATE", 153 Bytes: c.ParsedCertificate.Raw, 154 }) 155 if len(pem) == 0 { 156 return fmt.Errorf("failed to generate PEM: no body") 157 } 158 c.Certificate = string(pem) 159 160 return nil 161 }