mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-04 04:28:08 +00:00
Don't show field names when not needed
This commit is contained in:
@@ -37,8 +37,9 @@ func Backend() *framework.Backend {
|
|||||||
|
|
||||||
Paths: []*framework.Path{
|
Paths: []*framework.Path{
|
||||||
pathRoles(&b),
|
pathRoles(&b),
|
||||||
pathGenerateCA(&b),
|
pathGenerateRootCA(&b),
|
||||||
pathSignCA(&b),
|
pathGenerateIntermediateCA(&b),
|
||||||
|
pathSignIntermediateCA(&b),
|
||||||
pathSetCA(&b),
|
pathSetCA(&b),
|
||||||
pathConfigCA(&b),
|
pathConfigCA(&b),
|
||||||
pathConfigCRL(&b),
|
pathConfigCRL(&b),
|
||||||
|
|||||||
@@ -170,10 +170,16 @@ func validateCommonNames(req *logical.Request, commonNames []string, role *roleE
|
|||||||
func generateCert(b *backend,
|
func generateCert(b *backend,
|
||||||
role *roleEntry,
|
role *roleEntry,
|
||||||
signingBundle *certutil.ParsedCertBundle,
|
signingBundle *certutil.ParsedCertBundle,
|
||||||
|
isCA bool,
|
||||||
req *logical.Request,
|
req *logical.Request,
|
||||||
data *framework.FieldData) (*certutil.ParsedCertBundle, error) {
|
data *framework.FieldData) (*certutil.ParsedCertBundle, error) {
|
||||||
|
|
||||||
creationBundle, err := generateCreationBundle(b, role, signingBundle, req, data)
|
_, ok := data.GetOk("common_name")
|
||||||
|
if !ok {
|
||||||
|
return nil, certutil.UserError{Err: "The common_name field is required"}
|
||||||
|
}
|
||||||
|
|
||||||
|
creationBundle, err := generateCreationBundle(b, role, signingBundle, isCA, req, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -189,10 +195,11 @@ func generateCert(b *backend,
|
|||||||
func generateCSR(b *backend,
|
func generateCSR(b *backend,
|
||||||
role *roleEntry,
|
role *roleEntry,
|
||||||
signingBundle *certutil.ParsedCertBundle,
|
signingBundle *certutil.ParsedCertBundle,
|
||||||
|
isCA bool,
|
||||||
req *logical.Request,
|
req *logical.Request,
|
||||||
data *framework.FieldData) (*certutil.ParsedCSRBundle, error) {
|
data *framework.FieldData) (*certutil.ParsedCSRBundle, error) {
|
||||||
|
|
||||||
creationBundle, err := generateCreationBundle(b, role, signingBundle, req, data)
|
creationBundle, err := generateCreationBundle(b, role, signingBundle, isCA, req, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -209,10 +216,16 @@ func signCert(b *backend,
|
|||||||
role *roleEntry,
|
role *roleEntry,
|
||||||
signingBundle *certutil.ParsedCertBundle,
|
signingBundle *certutil.ParsedCertBundle,
|
||||||
csr *x509.CertificateRequest,
|
csr *x509.CertificateRequest,
|
||||||
|
isCA bool,
|
||||||
req *logical.Request,
|
req *logical.Request,
|
||||||
data *framework.FieldData) (*certutil.ParsedCertBundle, error) {
|
data *framework.FieldData) (*certutil.ParsedCertBundle, error) {
|
||||||
|
|
||||||
creationBundle, err := generateCreationBundle(b, role, signingBundle, req, data)
|
_, ok := data.GetOk("common_name")
|
||||||
|
if !ok {
|
||||||
|
return nil, certutil.UserError{Err: "The common_name field is required"}
|
||||||
|
}
|
||||||
|
|
||||||
|
creationBundle, err := generateCreationBundle(b, role, signingBundle, isCA, req, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -228,47 +241,56 @@ func signCert(b *backend,
|
|||||||
func generateCreationBundle(b *backend,
|
func generateCreationBundle(b *backend,
|
||||||
role *roleEntry,
|
role *roleEntry,
|
||||||
signingBundle *certutil.ParsedCertBundle,
|
signingBundle *certutil.ParsedCertBundle,
|
||||||
|
isCA bool,
|
||||||
req *logical.Request,
|
req *logical.Request,
|
||||||
data *framework.FieldData) (*certCreationBundle, error) {
|
data *framework.FieldData) (*certCreationBundle, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// Get the common name(s)
|
// Get the common name(s)
|
||||||
var commonNames []string
|
var cn string
|
||||||
cn := data.Get("common_name").(string)
|
cnInt, ok := data.GetOk("common_name")
|
||||||
if len(cn) == 0 {
|
if ok {
|
||||||
return nil, certutil.UserError{Err: "The common_name field is required"}
|
cn = cnInt.(string)
|
||||||
}
|
}
|
||||||
commonNames = []string{cn}
|
|
||||||
|
|
||||||
cnAlt := data.Get("alt_names").(string)
|
commonNames := []string{cn}
|
||||||
if len(cnAlt) != 0 {
|
cnAltInt, ok := data.GetOk("alt_names")
|
||||||
for _, v := range strings.Split(cnAlt, ",") {
|
if ok {
|
||||||
commonNames = append(commonNames, v)
|
cnAlt := cnAltInt.(string)
|
||||||
|
if len(cnAlt) != 0 {
|
||||||
|
for _, v := range strings.Split(cnAlt, ",") {
|
||||||
|
commonNames = append(commonNames, v)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get any IP SANs
|
// Get any IP SANs
|
||||||
ipSANs := []net.IP{}
|
ipSANs := []net.IP{}
|
||||||
|
ipAltInt, ok := data.GetOk("ip_sans")
|
||||||
ipAlt := data.Get("ip_sans").(string)
|
if ok {
|
||||||
if len(ipAlt) != 0 {
|
ipAlt := ipAltInt.(string)
|
||||||
if !role.AllowIPSANs {
|
if len(ipAlt) != 0 {
|
||||||
return nil, certutil.UserError{Err: fmt.Sprintf(
|
if !role.AllowIPSANs {
|
||||||
"IP Subject Alternative Names are not allowed in this role, but was provided %s", ipAlt)}
|
|
||||||
}
|
|
||||||
for _, v := range strings.Split(ipAlt, ",") {
|
|
||||||
parsedIP := net.ParseIP(v)
|
|
||||||
if parsedIP == nil {
|
|
||||||
return nil, certutil.UserError{Err: fmt.Sprintf(
|
return nil, certutil.UserError{Err: fmt.Sprintf(
|
||||||
"the value '%s' is not a valid IP address", v)}
|
"IP Subject Alternative Names are not allowed in this role, but was provided %s", ipAlt)}
|
||||||
|
}
|
||||||
|
for _, v := range strings.Split(ipAlt, ",") {
|
||||||
|
parsedIP := net.ParseIP(v)
|
||||||
|
if parsedIP == nil {
|
||||||
|
return nil, certutil.UserError{Err: fmt.Sprintf(
|
||||||
|
"the value '%s' is not a valid IP address", v)}
|
||||||
|
}
|
||||||
|
ipSANs = append(ipSANs, parsedIP)
|
||||||
}
|
}
|
||||||
ipSANs = append(ipSANs, parsedIP)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ttlField := data.Get("ttl").(string)
|
var ttlField string
|
||||||
if len(ttlField) == 0 {
|
ttlFieldInt, ok := data.GetOk("ttl")
|
||||||
|
if !ok {
|
||||||
ttlField = role.TTL
|
ttlField = role.TTL
|
||||||
|
} else {
|
||||||
|
ttlField = ttlFieldInt.(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
var ttl time.Duration
|
var ttl time.Duration
|
||||||
@@ -340,11 +362,13 @@ func generateCreationBundle(b *backend,
|
|||||||
Usage: usage,
|
Usage: usage,
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := req.Data["ca_type"]; ok {
|
if isCA {
|
||||||
creationBundle.CAType = req.Data["ca_type"].(string)
|
if _, ok := req.Data["ca_type"]; ok {
|
||||||
}
|
creationBundle.CAType = req.Data["ca_type"].(string)
|
||||||
if _, ok := req.Data["pki_address"]; ok {
|
}
|
||||||
creationBundle.PKIAddress = req.Data["pki_address"].(string)
|
if _, ok := req.Data["pki_address"]; ok {
|
||||||
|
creationBundle.PKIAddress = req.Data["pki_address"].(string)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return creationBundle, nil
|
return creationBundle, nil
|
||||||
|
|||||||
@@ -14,43 +14,7 @@ import (
|
|||||||
"github.com/hashicorp/vault/logical/framework"
|
"github.com/hashicorp/vault/logical/framework"
|
||||||
)
|
)
|
||||||
|
|
||||||
var generateAndSignSchema = map[string]*framework.FieldSchema{
|
var rootAndSignSchema = map[string]*framework.FieldSchema{
|
||||||
"type": &framework.FieldSchema{
|
|
||||||
Type: framework.TypeString,
|
|
||||||
Description: `Must be "self-signed" or "intermediate".
|
|
||||||
If set to "self-signed", a self-signed root CA
|
|
||||||
will be generated. If set to "intermediate", a
|
|
||||||
CSR will be returned for signing by the root.`,
|
|
||||||
},
|
|
||||||
|
|
||||||
"exported": &framework.FieldSchema{
|
|
||||||
Type: framework.TypeString,
|
|
||||||
Description: `Must be "internal" or "exported".
|
|
||||||
If set to "exported", the generated private
|
|
||||||
key will be returned. This is your *only*
|
|
||||||
chance to retrieve the private key!`,
|
|
||||||
},
|
|
||||||
|
|
||||||
"pki_address": &framework.FieldSchema{
|
|
||||||
Type: framework.TypeString,
|
|
||||||
Description: `The base URL of the PKI mount. For
|
|
||||||
"self-signed"; this is needed when
|
|
||||||
calling '/generate/' and should be
|
|
||||||
the base URL of the mount where you
|
|
||||||
are running this command, e.g.
|
|
||||||
"https://vault.example.com/v1/root_pki".
|
|
||||||
For "intermediate", this is needed
|
|
||||||
when calling '/sign/' and should be
|
|
||||||
the base URL of the destination
|
|
||||||
mount of the certificate, e.g.
|
|
||||||
"https://vault.example.com/v1/intermediate_pki".
|
|
||||||
For HA setups, the given host name
|
|
||||||
should be the address that can always
|
|
||||||
be used to contact the leader. This is
|
|
||||||
is used for generating the CA/CRL URLs in
|
|
||||||
the certificate. Required as specified.`,
|
|
||||||
},
|
|
||||||
|
|
||||||
"common_name": &framework.FieldSchema{
|
"common_name": &framework.FieldSchema{
|
||||||
Type: framework.TypeString,
|
Type: framework.TypeString,
|
||||||
Description: `The requested common name; if you want more than
|
Description: `The requested common name; if you want more than
|
||||||
@@ -70,26 +34,16 @@ in a comma-delimited list`,
|
|||||||
common-delimited list`,
|
common-delimited list`,
|
||||||
},
|
},
|
||||||
|
|
||||||
"key_bits": &framework.FieldSchema{
|
|
||||||
Type: framework.TypeInt,
|
|
||||||
Default: 2048,
|
|
||||||
Description: `The number of bits to use. You will almost
|
|
||||||
certainly want to change this if you adjust
|
|
||||||
the key_type.`,
|
|
||||||
},
|
|
||||||
|
|
||||||
"ttl": &framework.FieldSchema{
|
"ttl": &framework.FieldSchema{
|
||||||
Type: framework.TypeString,
|
Type: framework.TypeString,
|
||||||
Description: `The requested Time To Live for the certificate;
|
Description: `The requested Time To Live for the certificate;
|
||||||
sets the expiration date. If not specified
|
sets the expiration date. If not specified
|
||||||
the role default, backend default, or system
|
the role default, backend default, or system
|
||||||
default TTL is used, in that order. Cannot
|
default TTL is used, in that order. Cannot
|
||||||
be larger than the mount max TTL.`,
|
be larger than the mount max TTL. Note:
|
||||||
},
|
this only has an effect when generating
|
||||||
|
a CA cert or signing a CA cert, not when
|
||||||
"csr": &framework.FieldSchema{
|
creating a CSR for an intermediate CA.`,
|
||||||
Type: framework.TypeString,
|
|
||||||
Description: `PEM-format CSR to be signed.`,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,26 +88,88 @@ endpoint, just the signed certificate.`,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func pathGenerateCA(b *backend) *framework.Path {
|
func pathGenerateRootCA(b *backend) *framework.Path {
|
||||||
return &framework.Path{
|
ret := &framework.Path{
|
||||||
Pattern: "config/ca/generate/" + framework.GenericNameRegex("type") + "/" + framework.GenericNameRegex("exported"),
|
Pattern: "config/ca/generate/root/" + framework.GenericNameRegex("exported"),
|
||||||
|
|
||||||
Fields: generateAndSignSchema,
|
Fields: rootAndSignSchema,
|
||||||
|
|
||||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||||
logical.WriteOperation: b.pathCAGenerateWrite,
|
logical.WriteOperation: b.pathCAGenerateRoot,
|
||||||
},
|
},
|
||||||
|
|
||||||
HelpSynopsis: pathConfigCAGenerateHelpSyn,
|
HelpSynopsis: pathConfigCAGenerateHelpSyn,
|
||||||
HelpDescription: pathConfigCAGenerateHelpDesc,
|
HelpDescription: pathConfigCAGenerateHelpDesc,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret.Fields["pki_address"] = &framework.FieldSchema{
|
||||||
|
Type: framework.TypeString,
|
||||||
|
Description: `The base URL of the PKI mount, e.g.
|
||||||
|
"https://vault.example.com/v1/root_pki".
|
||||||
|
For HA setups, the given host name
|
||||||
|
should be the address that can always
|
||||||
|
be used to contact the leader, as this is
|
||||||
|
is used for generating the CA/CRL URLs in
|
||||||
|
the certificate.`,
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.Fields["exported"] = &framework.FieldSchema{
|
||||||
|
Type: framework.TypeString,
|
||||||
|
Description: `Must be "internal" or "exported".
|
||||||
|
If set to "exported", the generated private
|
||||||
|
key will be returned. This is your *only*
|
||||||
|
chance to retrieve the private key!`,
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.Fields["key_bits"] = &framework.FieldSchema{
|
||||||
|
Type: framework.TypeInt,
|
||||||
|
Default: 2048,
|
||||||
|
Description: `The number of bits to use. You will almost
|
||||||
|
certainly want to change this if you adjust
|
||||||
|
the key_type.`,
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func pathSignCA(b *backend) *framework.Path {
|
func pathGenerateIntermediateCA(b *backend) *framework.Path {
|
||||||
return &framework.Path{
|
ret := &framework.Path{
|
||||||
|
Pattern: "config/ca/generate/intermediate/" + framework.GenericNameRegex("exported"),
|
||||||
|
|
||||||
|
Fields: map[string]*framework.FieldSchema{},
|
||||||
|
|
||||||
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||||
|
logical.WriteOperation: b.pathCAGenerateIntermediate,
|
||||||
|
},
|
||||||
|
|
||||||
|
HelpSynopsis: pathConfigCAGenerateHelpSyn,
|
||||||
|
HelpDescription: pathConfigCAGenerateHelpDesc,
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.Fields["exported"] = &framework.FieldSchema{
|
||||||
|
Type: framework.TypeString,
|
||||||
|
Description: `Must be "internal" or "exported".
|
||||||
|
If set to "exported", the generated private
|
||||||
|
key will be returned. This is your *only*
|
||||||
|
chance to retrieve the private key!`,
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.Fields["key_bits"] = &framework.FieldSchema{
|
||||||
|
Type: framework.TypeInt,
|
||||||
|
Default: 2048,
|
||||||
|
Description: `The number of bits to use. You will almost
|
||||||
|
certainly want to change this if you adjust
|
||||||
|
the key_type.`,
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func pathSignIntermediateCA(b *backend) *framework.Path {
|
||||||
|
ret := &framework.Path{
|
||||||
Pattern: "config/ca/sign",
|
Pattern: "config/ca/sign",
|
||||||
|
|
||||||
Fields: generateAndSignSchema,
|
Fields: rootAndSignSchema,
|
||||||
|
|
||||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||||
logical.WriteOperation: b.pathCASignWrite,
|
logical.WriteOperation: b.pathCASignWrite,
|
||||||
@@ -162,9 +178,28 @@ func pathSignCA(b *backend) *framework.Path {
|
|||||||
HelpSynopsis: pathConfigCASignHelpSyn,
|
HelpSynopsis: pathConfigCASignHelpSyn,
|
||||||
HelpDescription: pathConfigCASignHelpDesc,
|
HelpDescription: pathConfigCASignHelpDesc,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret.Fields["pki_address"] = &framework.FieldSchema{
|
||||||
|
Type: framework.TypeString,
|
||||||
|
Description: `The base URL of the *destination*
|
||||||
|
PKI mount, e.g.
|
||||||
|
"https://vault.example.com/v1/intermediate_pki".
|
||||||
|
For HA setups, the given host name
|
||||||
|
should be the address that can always
|
||||||
|
be used to contact the leader, as this is
|
||||||
|
is used for generating the CA/CRL URLs in
|
||||||
|
the certificate.`,
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.Fields["csr"] = &framework.FieldSchema{
|
||||||
|
Type: framework.TypeString,
|
||||||
|
Description: `PEM-format CSR to be signed.`,
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *backend) pathCAGenerateWrite(
|
func (b *backend) pathCAGenerateRoot(
|
||||||
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||||
var err error
|
var err error
|
||||||
exported := data.Get("exported").(string)
|
exported := data.Get("exported").(string)
|
||||||
@@ -176,39 +211,28 @@ func (b *backend) pathCAGenerateWrite(
|
|||||||
"The \"exported\" path parameter must be \"internal\" or \"exported\"")), nil
|
"The \"exported\" path parameter must be \"internal\" or \"exported\"")), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
genType := data.Get("type").(string)
|
req.Data["ca_type"] = "root"
|
||||||
switch genType {
|
|
||||||
case "self-signed":
|
pkiAddress := strings.ToLower(data.Get("pki_address").(string))
|
||||||
case "intermediate":
|
switch {
|
||||||
default:
|
case len(pkiAddress) == 0:
|
||||||
return logical.ErrorResponse(fmt.Sprintf(
|
return logical.ErrorResponse(fmt.Sprintf(
|
||||||
"The \"type\" path parameter must be \"self-signed\" or \"intermediate\"")), nil
|
"\"pki_address\" cannot be empty")), nil
|
||||||
|
case !strings.HasPrefix(pkiAddress, "http"):
|
||||||
|
return logical.ErrorResponse(fmt.Sprintf(
|
||||||
|
"\"pki_address\" must be a URL")), nil
|
||||||
|
case !strings.Contains(pkiAddress, "/v1/"):
|
||||||
|
return logical.ErrorResponse(fmt.Sprintf(
|
||||||
|
"\"pki_address\" needs to be the path to the PKI mount, not the base Vault path")), nil
|
||||||
|
case !strings.Contains(pkiAddress, "/v1/"+req.MountPoint[:len(req.MountPoint)-1]):
|
||||||
|
return logical.ErrorResponse(fmt.Sprintf(
|
||||||
|
"\"pki_address\" needs to be the path to this mount")), nil
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(pkiAddress, "/") {
|
||||||
|
pkiAddress = pkiAddress[:len(pkiAddress)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Data["ca_type"] = genType
|
req.Data["pki_address"] = pkiAddress
|
||||||
|
|
||||||
if genType == "self-signed" {
|
|
||||||
pkiAddress := strings.ToLower(data.Get("pki_address").(string))
|
|
||||||
switch {
|
|
||||||
case len(pkiAddress) == 0:
|
|
||||||
return logical.ErrorResponse(fmt.Sprintf(
|
|
||||||
"\"pki_address\" cannot be empty")), nil
|
|
||||||
case !strings.HasPrefix(pkiAddress, "http"):
|
|
||||||
return logical.ErrorResponse(fmt.Sprintf(
|
|
||||||
"\"pki_address\" must be a URL")), nil
|
|
||||||
case !strings.Contains(pkiAddress, "/v1/"):
|
|
||||||
return logical.ErrorResponse(fmt.Sprintf(
|
|
||||||
"\"pki_address\" needs to be the path to the PKI mount, not the base Vault path")), nil
|
|
||||||
case !strings.Contains(pkiAddress, "/v1/"+req.MountPoint[:len(req.MountPoint)-1]):
|
|
||||||
return logical.ErrorResponse(fmt.Sprintf(
|
|
||||||
"\"pki_address\" needs to be the path to this mount")), nil
|
|
||||||
}
|
|
||||||
if strings.HasSuffix(pkiAddress, "/") {
|
|
||||||
pkiAddress = pkiAddress[:len(pkiAddress)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Data["pki_address"] = pkiAddress
|
|
||||||
}
|
|
||||||
|
|
||||||
role := &roleEntry{
|
role := &roleEntry{
|
||||||
TTL: data.Get("ttl").(string),
|
TTL: data.Get("ttl").(string),
|
||||||
@@ -219,21 +243,6 @@ func (b *backend) pathCAGenerateWrite(
|
|||||||
EnforceHostnames: false,
|
EnforceHostnames: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
maxSystemTTL := b.System().MaxLeaseTTL()
|
|
||||||
|
|
||||||
ttl := b.System().DefaultLeaseTTL()
|
|
||||||
if len(role.TTL) != 0 {
|
|
||||||
ttl, err = time.ParseDuration(role.TTL)
|
|
||||||
if err != nil {
|
|
||||||
return logical.ErrorResponse(fmt.Sprintf(
|
|
||||||
"Invalid ttl: %s", err)), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ttl > maxSystemTTL {
|
|
||||||
return logical.ErrorResponse(fmt.Sprintf(
|
|
||||||
"\"ttl\" value must be less than mount max of %d seconds", maxSystemTTL/time.Second)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch role.KeyBits {
|
switch role.KeyBits {
|
||||||
case 0:
|
case 0:
|
||||||
role.KeyBits = 2048
|
role.KeyBits = 2048
|
||||||
@@ -246,105 +255,135 @@ func (b *backend) pathCAGenerateWrite(
|
|||||||
}
|
}
|
||||||
|
|
||||||
var resp *logical.Response
|
var resp *logical.Response
|
||||||
switch genType {
|
parsedBundle, err := generateCert(b, role, nil, true, req, data)
|
||||||
case "self-signed":
|
if err != nil {
|
||||||
parsedBundle, err := generateCert(b, role, nil, req, data)
|
switch err.(type) {
|
||||||
if err != nil {
|
case certutil.UserError:
|
||||||
switch err.(type) {
|
return logical.ErrorResponse(err.Error()), nil
|
||||||
case certutil.UserError:
|
case certutil.InternalError:
|
||||||
return logical.ErrorResponse(err.Error()), nil
|
|
||||||
case certutil.InternalError:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cb, err := parsedBundle.ToCertBundle()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Error converting raw cert bundle to cert bundle: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp = &logical.Response{
|
|
||||||
Data: map[string]interface{}{
|
|
||||||
"serial_number": cb.SerialNumber,
|
|
||||||
"certificate": cb.Certificate,
|
|
||||||
"issuing_ca": cb.IssuingCA,
|
|
||||||
"expiration": int(parsedBundle.Certificate.NotAfter.Unix()),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if exported == "exported" {
|
|
||||||
resp.Data["private_key"] = cb.PrivateKey
|
|
||||||
resp.Data["private_key_type"] = cb.PrivateKeyType
|
|
||||||
}
|
|
||||||
|
|
||||||
entry, err := logical.StorageEntryJSON("config/ca_bundle", cb)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = req.Storage.Put(entry)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// For ease of later use, also store just the certificate at a known
|
cb, err := parsedBundle.ToCertBundle()
|
||||||
// location, plus a blank CRL
|
if err != nil {
|
||||||
entry.Key = "ca"
|
return nil, fmt.Errorf("Error converting raw cert bundle to cert bundle: %s", err)
|
||||||
entry.Value = parsedBundle.CertificateBytes
|
}
|
||||||
err = req.Storage.Put(entry)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
entry.Key = "crl"
|
resp = &logical.Response{
|
||||||
entry.Value = []byte{}
|
Data: map[string]interface{}{
|
||||||
err = req.Storage.Put(entry)
|
"serial_number": cb.SerialNumber,
|
||||||
if err != nil {
|
"certificate": cb.Certificate,
|
||||||
return nil, err
|
"issuing_ca": cb.IssuingCA,
|
||||||
}
|
"expiration": int(parsedBundle.Certificate.NotAfter.Unix()),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
case "intermediate":
|
if exported == "exported" {
|
||||||
parsedBundle, err := generateCSR(b, role, nil, req, data)
|
resp.Data["private_key"] = cb.PrivateKey
|
||||||
if err != nil {
|
resp.Data["private_key_type"] = cb.PrivateKeyType
|
||||||
switch err.(type) {
|
}
|
||||||
case certutil.UserError:
|
|
||||||
return logical.ErrorResponse(err.Error()), nil
|
|
||||||
case certutil.InternalError:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
csrb, err := parsedBundle.ToCSRBundle()
|
entry, err := logical.StorageEntryJSON("config/ca_bundle", cb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Error converting raw CSR bundle to CSR bundle: %s", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
err = req.Storage.Put(entry)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
resp = &logical.Response{
|
// For ease of later use, also store just the certificate at a known
|
||||||
Data: map[string]interface{}{
|
// location, plus a blank CRL
|
||||||
"csr": csrb.CSR,
|
entry.Key = "ca"
|
||||||
},
|
entry.Value = parsedBundle.CertificateBytes
|
||||||
}
|
err = req.Storage.Put(entry)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if exported == "exported" {
|
entry.Key = "crl"
|
||||||
resp.Data["private_key"] = csrb.PrivateKey
|
entry.Value = []byte{}
|
||||||
resp.Data["private_key_type"] = csrb.PrivateKeyType
|
err = req.Storage.Put(entry)
|
||||||
}
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
cb := &certutil.CertBundle{
|
return resp, nil
|
||||||
PrivateKey: csrb.PrivateKey,
|
}
|
||||||
PrivateKeyType: csrb.PrivateKeyType,
|
|
||||||
}
|
|
||||||
|
|
||||||
entry, err := logical.StorageEntryJSON("config/ca_bundle", cb)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = req.Storage.Put(entry)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
|
func (b *backend) pathCAGenerateIntermediate(
|
||||||
|
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||||
|
var err error
|
||||||
|
exported := data.Get("exported").(string)
|
||||||
|
switch exported {
|
||||||
|
case "exported":
|
||||||
|
case "internal":
|
||||||
default:
|
default:
|
||||||
return logical.ErrorResponse("Unknown generation type"), nil
|
return logical.ErrorResponse(fmt.Sprintf(
|
||||||
|
"The \"exported\" path parameter must be \"internal\" or \"exported\"")), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Data["ca_type"] = "intermediate"
|
||||||
|
|
||||||
|
role := &roleEntry{
|
||||||
|
KeyType: "rsa",
|
||||||
|
KeyBits: data.Get("key_bits").(int),
|
||||||
|
AllowLocalhost: true,
|
||||||
|
AllowAnyName: true,
|
||||||
|
EnforceHostnames: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
switch role.KeyBits {
|
||||||
|
case 0:
|
||||||
|
role.KeyBits = 2048
|
||||||
|
case 1024:
|
||||||
|
case 2048:
|
||||||
|
case 4096:
|
||||||
|
default:
|
||||||
|
return logical.ErrorResponse(fmt.Sprintf(
|
||||||
|
"\"key_bits\" must be 1024, 2048, or 4096")), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp *logical.Response
|
||||||
|
parsedBundle, err := generateCSR(b, role, nil, true, req, data)
|
||||||
|
if err != nil {
|
||||||
|
switch err.(type) {
|
||||||
|
case certutil.UserError:
|
||||||
|
return logical.ErrorResponse(err.Error()), nil
|
||||||
|
case certutil.InternalError:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
csrb, err := parsedBundle.ToCSRBundle()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error converting raw CSR bundle to CSR bundle: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp = &logical.Response{
|
||||||
|
Data: map[string]interface{}{
|
||||||
|
"csr": csrb.CSR,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if exported == "exported" {
|
||||||
|
resp.Data["private_key"] = csrb.PrivateKey
|
||||||
|
resp.Data["private_key_type"] = csrb.PrivateKeyType
|
||||||
|
}
|
||||||
|
|
||||||
|
cb := &certutil.CertBundle{
|
||||||
|
PrivateKey: csrb.PrivateKey,
|
||||||
|
PrivateKeyType: csrb.PrivateKeyType,
|
||||||
|
}
|
||||||
|
|
||||||
|
entry, err := logical.StorageEntryJSON("config/ca_bundle", cb)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = req.Storage.Put(entry)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
@@ -425,7 +464,7 @@ func (b *backend) pathCASignWrite(
|
|||||||
"Error fetching CA certificate: %s", caErr)}
|
"Error fetching CA certificate: %s", caErr)}
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedBundle, err := signCert(b, role, signingBundle, csr, req, data)
|
parsedBundle, err := signCert(b, role, signingBundle, csr, true, req, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch err.(type) {
|
switch err.(type) {
|
||||||
case certutil.UserError:
|
case certutil.UserError:
|
||||||
|
|||||||
@@ -10,45 +10,65 @@ import (
|
|||||||
"github.com/hashicorp/vault/logical/framework"
|
"github.com/hashicorp/vault/logical/framework"
|
||||||
)
|
)
|
||||||
|
|
||||||
func pathIssue(b *backend) *framework.Path {
|
var issueAndSignSchema = map[string]*framework.FieldSchema{
|
||||||
return &framework.Path{
|
"role": &framework.FieldSchema{
|
||||||
Pattern: "issue/" + framework.GenericNameRegex("role"),
|
Type: framework.TypeString,
|
||||||
Fields: map[string]*framework.FieldSchema{
|
Description: `The desired role with configuration for this
|
||||||
"role": &framework.FieldSchema{
|
|
||||||
Type: framework.TypeString,
|
|
||||||
Description: `The desired role with configuration for this
|
|
||||||
request`,
|
request`,
|
||||||
},
|
},
|
||||||
|
|
||||||
"common_name": &framework.FieldSchema{
|
"common_name": &framework.FieldSchema{
|
||||||
Type: framework.TypeString,
|
Type: framework.TypeString,
|
||||||
Description: `The requested common name; if you want more than
|
Description: `The requested common name; if you want more than
|
||||||
one, specify the alternative names in the
|
one, specify the alternative names in the
|
||||||
alt_names map`,
|
alt_names map`,
|
||||||
},
|
},
|
||||||
|
|
||||||
"alt_names": &framework.FieldSchema{
|
"alt_names": &framework.FieldSchema{
|
||||||
Type: framework.TypeString,
|
Type: framework.TypeString,
|
||||||
Description: `The requested Subject Alternative Names, if any,
|
Description: `The requested Subject Alternative Names, if any,
|
||||||
in a comma-delimited list`,
|
in a comma-delimited list`,
|
||||||
},
|
},
|
||||||
|
|
||||||
"ip_sans": &framework.FieldSchema{
|
"ip_sans": &framework.FieldSchema{
|
||||||
Type: framework.TypeString,
|
Type: framework.TypeString,
|
||||||
Description: `The requested IP SANs, if any, in a
|
Description: `The requested IP SANs, if any, in a
|
||||||
common-delimited list`,
|
common-delimited list`,
|
||||||
},
|
},
|
||||||
|
|
||||||
"ttl": &framework.FieldSchema{
|
"ttl": &framework.FieldSchema{
|
||||||
Type: framework.TypeString,
|
Type: framework.TypeString,
|
||||||
Description: `The requested Time To Live for the certificate;
|
Description: `The requested Time To Live for the certificate;
|
||||||
sets the expiration date. If not specified
|
sets the expiration date. If not specified
|
||||||
the role default, backend default, or system
|
the role default, backend default, or system
|
||||||
default TTL is used, in that order. Cannot
|
default TTL is used, in that order. Cannot
|
||||||
be later than the role max TTL.`,
|
be later than the role max TTL.`,
|
||||||
},
|
},
|
||||||
|
"csr": &framework.FieldSchema{
|
||||||
|
Type: framework.TypeString,
|
||||||
|
Description: `PEM-format CSR to be signed.`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func pathIssue(b *backend) *framework.Path {
|
||||||
|
return &framework.Path{
|
||||||
|
Pattern: "issue/" + framework.GenericNameRegex("role"),
|
||||||
|
Fields: issueAndSignSchema,
|
||||||
|
|
||||||
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||||
|
logical.WriteOperation: b.pathIssueCert,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
HelpSynopsis: pathIssueCertHelpSyn,
|
||||||
|
HelpDescription: pathIssueCertHelpDesc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func pathSign(b *backend) *framework.Path {
|
||||||
|
return &framework.Path{
|
||||||
|
Pattern: "sign/" + framework.GenericNameRegex("role"),
|
||||||
|
Fields: issueAndSignSchema,
|
||||||
|
|
||||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||||
logical.WriteOperation: b.pathIssueCert,
|
logical.WriteOperation: b.pathIssueCert,
|
||||||
},
|
},
|
||||||
@@ -82,13 +102,7 @@ func (b *backend) pathIssueCert(
|
|||||||
"Error fetching CA certificate: %s", caErr)}
|
"Error fetching CA certificate: %s", caErr)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't allow these on the standard path. Ideally we should determine
|
parsedBundle, err := generateCert(b, role, signingBundle, false, req, data)
|
||||||
// this internally once we get SudoPrivilege from System() working
|
|
||||||
// for non-TokenStore
|
|
||||||
delete(req.Data, "ca_type")
|
|
||||||
delete(req.Data, "pki_address")
|
|
||||||
|
|
||||||
parsedBundle, err := generateCert(b, role, signingBundle, req, data)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch err.(type) {
|
switch err.(type) {
|
||||||
case certutil.UserError:
|
case certutil.UserError:
|
||||||
|
|||||||
Reference in New Issue
Block a user