2015-05-15 16:13:05 +00:00
|
|
|
package pki
|
|
|
|
|
|
|
|
import (
|
2018-01-08 18:31:38 +00:00
|
|
|
"context"
|
2016-06-22 20:08:24 +00:00
|
|
|
"crypto/x509"
|
2015-05-15 16:13:05 +00:00
|
|
|
"fmt"
|
2015-12-01 04:49:11 +00:00
|
|
|
"strings"
|
2015-05-15 16:13:05 +00:00
|
|
|
"time"
|
|
|
|
|
2018-02-03 01:28:25 +00:00
|
|
|
"github.com/hashicorp/vault/helper/consts"
|
2017-03-07 16:21:22 +00:00
|
|
|
"github.com/hashicorp/vault/helper/parseutil"
|
2015-05-15 16:13:05 +00:00
|
|
|
"github.com/hashicorp/vault/logical"
|
|
|
|
"github.com/hashicorp/vault/logical/framework"
|
|
|
|
)
|
|
|
|
|
2016-01-28 20:18:07 +00:00
|
|
|
func pathListRoles(b *backend) *framework.Path {
|
|
|
|
return &framework.Path{
|
|
|
|
Pattern: "roles/?$",
|
|
|
|
|
|
|
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
|
|
|
logical.ListOperation: b.pathRoleList,
|
|
|
|
},
|
|
|
|
|
2016-06-20 23:51:04 +00:00
|
|
|
HelpSynopsis: pathListRolesHelpSyn,
|
|
|
|
HelpDescription: pathListRolesHelpDesc,
|
2016-01-28 20:18:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-15 16:13:05 +00:00
|
|
|
func pathRoles(b *backend) *framework.Path {
|
|
|
|
return &framework.Path{
|
2015-08-21 07:56:13 +00:00
|
|
|
Pattern: "roles/" + framework.GenericNameRegex("name"),
|
2015-05-15 16:13:05 +00:00
|
|
|
Fields: map[string]*framework.FieldSchema{
|
|
|
|
"name": &framework.FieldSchema{
|
|
|
|
Type: framework.TypeString,
|
2015-06-18 14:44:02 +00:00
|
|
|
Description: "Name of the role",
|
2015-05-15 16:13:05 +00:00
|
|
|
},
|
|
|
|
|
2015-08-27 19:24:37 +00:00
|
|
|
"ttl": &framework.FieldSchema{
|
2017-08-31 19:46:13 +00:00
|
|
|
Type: framework.TypeDurationSecond,
|
2015-08-27 19:24:37 +00:00
|
|
|
Default: "",
|
|
|
|
Description: `The lease duration if no specific lease duration is
|
|
|
|
requested. The lease duration controls the expiration
|
|
|
|
of certificates issued by this backend. Defaults to
|
|
|
|
the value of max_ttl.`,
|
|
|
|
},
|
|
|
|
|
|
|
|
"max_ttl": &framework.FieldSchema{
|
2015-05-15 16:13:05 +00:00
|
|
|
Type: framework.TypeString,
|
|
|
|
Default: "",
|
2015-08-27 19:24:37 +00:00
|
|
|
Description: "The maximum allowed lease duration",
|
2015-05-15 16:13:05 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
"allow_localhost": &framework.FieldSchema{
|
2015-06-18 14:44:02 +00:00
|
|
|
Type: framework.TypeBool,
|
|
|
|
Default: true,
|
|
|
|
Description: `Whether to allow "localhost" as a valid common
|
|
|
|
name in a request`,
|
2015-05-15 16:13:05 +00:00
|
|
|
},
|
|
|
|
|
2015-12-01 04:49:11 +00:00
|
|
|
"allowed_domains": &framework.FieldSchema{
|
2017-12-11 18:13:35 +00:00
|
|
|
Type: framework.TypeCommaStringSlice,
|
2015-06-18 14:44:02 +00:00
|
|
|
Description: `If set, clients can request certificates for
|
2015-12-01 04:49:11 +00:00
|
|
|
subdomains directly beneath these domains, including
|
|
|
|
the wildcard subdomains. See the documentation for more
|
2017-12-11 18:13:35 +00:00
|
|
|
information. This parameter accepts a comma-separated
|
|
|
|
string or list of domains.`,
|
2015-10-02 16:22:02 +00:00
|
|
|
},
|
|
|
|
|
2015-12-01 04:49:11 +00:00
|
|
|
"allow_bare_domains": &framework.FieldSchema{
|
2015-10-02 16:22:02 +00:00
|
|
|
Type: framework.TypeBool,
|
|
|
|
Default: false,
|
|
|
|
Description: `If set, clients can request certificates
|
2015-12-01 04:49:11 +00:00
|
|
|
for the base domains themselves, e.g. "example.com".
|
|
|
|
This is a separate option as in some cases this can
|
|
|
|
be considered a security threat.`,
|
2015-05-15 16:13:05 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
"allow_subdomains": &framework.FieldSchema{
|
2015-06-18 14:44:02 +00:00
|
|
|
Type: framework.TypeBool,
|
|
|
|
Default: false,
|
|
|
|
Description: `If set, clients can request certificates for
|
|
|
|
subdomains of the CNs allowed by the other role options,
|
|
|
|
including wildcard subdomains. See the documentation for
|
|
|
|
more information.`,
|
2015-05-15 16:13:05 +00:00
|
|
|
},
|
|
|
|
|
2017-05-01 14:40:18 +00:00
|
|
|
"allow_glob_domains": &framework.FieldSchema{
|
|
|
|
Type: framework.TypeBool,
|
|
|
|
Default: false,
|
|
|
|
Description: `If set, domains specified in "allowed_domains"
|
|
|
|
can include glob patterns, e.g. "ftp*.example.com". See
|
|
|
|
the documentation for more information.`,
|
|
|
|
},
|
|
|
|
|
2015-05-15 16:13:05 +00:00
|
|
|
"allow_any_name": &framework.FieldSchema{
|
2015-06-18 14:44:02 +00:00
|
|
|
Type: framework.TypeBool,
|
|
|
|
Default: false,
|
|
|
|
Description: `If set, clients can request certificates for
|
|
|
|
any CN they like. See the documentation for more
|
|
|
|
information.`,
|
2015-05-15 16:13:05 +00:00
|
|
|
},
|
|
|
|
|
2015-08-20 21:33:37 +00:00
|
|
|
"enforce_hostnames": &framework.FieldSchema{
|
|
|
|
Type: framework.TypeBool,
|
2015-11-16 15:10:03 +00:00
|
|
|
Default: true,
|
2015-08-20 21:33:37 +00:00
|
|
|
Description: `If set, only valid host names are allowed for
|
2015-11-16 15:10:03 +00:00
|
|
|
CN and SANs. Defaults to true.`,
|
2015-08-20 21:33:37 +00:00
|
|
|
},
|
|
|
|
|
2015-05-15 16:13:05 +00:00
|
|
|
"allow_ip_sans": &framework.FieldSchema{
|
2015-06-18 14:44:02 +00:00
|
|
|
Type: framework.TypeBool,
|
|
|
|
Default: true,
|
|
|
|
Description: `If set, IP Subject Alternative Names are allowed.
|
|
|
|
Any valid IP is accepted.`,
|
2015-05-15 16:13:05 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
"server_flag": &framework.FieldSchema{
|
2015-06-18 14:44:02 +00:00
|
|
|
Type: framework.TypeBool,
|
|
|
|
Default: true,
|
2015-08-29 13:03:02 +00:00
|
|
|
Description: `If set, certificates are flagged for server auth use.
|
2015-06-18 14:44:02 +00:00
|
|
|
Defaults to true.`,
|
2015-05-15 16:13:05 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
"client_flag": &framework.FieldSchema{
|
2015-06-18 14:44:02 +00:00
|
|
|
Type: framework.TypeBool,
|
|
|
|
Default: true,
|
2015-08-29 13:03:02 +00:00
|
|
|
Description: `If set, certificates are flagged for client auth use.
|
2015-06-18 14:44:02 +00:00
|
|
|
Defaults to true.`,
|
2015-05-15 16:13:05 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
"code_signing_flag": &framework.FieldSchema{
|
2015-06-18 14:44:02 +00:00
|
|
|
Type: framework.TypeBool,
|
|
|
|
Default: false,
|
|
|
|
Description: `If set, certificates are flagged for code signing
|
|
|
|
use. Defaults to false.`,
|
2015-05-15 16:13:05 +00:00
|
|
|
},
|
|
|
|
|
2015-10-02 15:55:30 +00:00
|
|
|
"email_protection_flag": &framework.FieldSchema{
|
2015-08-29 13:03:02 +00:00
|
|
|
Type: framework.TypeBool,
|
|
|
|
Default: false,
|
|
|
|
Description: `If set, certificates are flagged for email
|
|
|
|
protection use. Defaults to false.`,
|
|
|
|
},
|
|
|
|
|
2015-05-15 16:13:05 +00:00
|
|
|
"key_type": &framework.FieldSchema{
|
2015-06-18 14:44:02 +00:00
|
|
|
Type: framework.TypeString,
|
|
|
|
Default: "rsa",
|
|
|
|
Description: `The type of key to use; defaults to RSA. "rsa"
|
|
|
|
and "ec" are the only valid values.`,
|
2015-05-15 16:13:05 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
"key_bits": &framework.FieldSchema{
|
2015-06-18 14:44:02 +00:00
|
|
|
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.`,
|
2015-05-15 16:13:05 +00:00
|
|
|
},
|
2015-10-09 17:45:17 +00:00
|
|
|
|
2016-06-22 20:08:24 +00:00
|
|
|
"key_usage": &framework.FieldSchema{
|
2017-12-11 18:13:35 +00:00
|
|
|
Type: framework.TypeCommaStringSlice,
|
|
|
|
Default: []string{"DigitalSignature", "KeyAgreement", "KeyEncipherment"},
|
|
|
|
Description: `A comma-separated string or list of key usages (not extended
|
2016-06-22 20:08:24 +00:00
|
|
|
key usages). Valid values can be found at
|
|
|
|
https://golang.org/pkg/crypto/x509/#KeyUsage
|
|
|
|
-- simply drop the "KeyUsage" part of the name.
|
|
|
|
To remove all key usages from being set, set
|
2017-12-11 18:13:35 +00:00
|
|
|
this value to an empty list.`,
|
2016-06-22 20:08:24 +00:00
|
|
|
},
|
|
|
|
|
2015-10-09 17:45:17 +00:00
|
|
|
"use_csr_common_name": &framework.FieldSchema{
|
2015-11-12 16:24:32 +00:00
|
|
|
Type: framework.TypeBool,
|
2015-11-16 15:10:03 +00:00
|
|
|
Default: true,
|
2015-10-09 17:45:17 +00:00
|
|
|
Description: `If set, when used with a signing profile,
|
|
|
|
the common name in the CSR will be used. This
|
|
|
|
does *not* include any requested Subject Alternative
|
2015-11-16 15:10:03 +00:00
|
|
|
Names. Defaults to true.`,
|
2015-10-09 17:45:17 +00:00
|
|
|
},
|
2017-01-23 17:44:45 +00:00
|
|
|
|
2017-03-15 18:38:18 +00:00
|
|
|
"use_csr_sans": &framework.FieldSchema{
|
|
|
|
Type: framework.TypeBool,
|
|
|
|
Default: true,
|
|
|
|
Description: `If set, when used with a signing profile,
|
|
|
|
the SANs in the CSR will be used. This does *not*
|
|
|
|
include the Common Name (cn). Defaults to true.`,
|
|
|
|
},
|
|
|
|
|
2017-01-23 17:44:45 +00:00
|
|
|
"ou": &framework.FieldSchema{
|
2018-01-17 16:53:49 +00:00
|
|
|
Type: framework.TypeCommaStringSlice,
|
2017-01-23 17:44:45 +00:00
|
|
|
Description: `If set, the OU (OrganizationalUnit) will be set to
|
2017-02-16 06:04:29 +00:00
|
|
|
this value in certificates issued by this role.`,
|
|
|
|
},
|
|
|
|
|
|
|
|
"organization": &framework.FieldSchema{
|
2018-01-17 16:53:49 +00:00
|
|
|
Type: framework.TypeCommaStringSlice,
|
2017-02-16 06:04:29 +00:00
|
|
|
Description: `If set, the O (Organization) will be set to
|
2017-01-23 17:44:45 +00:00
|
|
|
this value in certificates issued by this role.`,
|
|
|
|
},
|
2017-02-24 17:12:40 +00:00
|
|
|
|
|
|
|
"generate_lease": &framework.FieldSchema{
|
|
|
|
Type: framework.TypeBool,
|
|
|
|
Default: false,
|
|
|
|
Description: `
|
|
|
|
If set, certificates issued/signed against this role will have Vault leases
|
|
|
|
attached to them. Defaults to "false". Certificates can be added to the CRL by
|
|
|
|
"vault revoke <lease_id>" when certificates are associated with leases. It can
|
|
|
|
also be done using the "pki/revoke" endpoint. However, when lease generation is
|
|
|
|
disabled, invoking "pki/revoke" would be the only way to add the certificates
|
|
|
|
to the CRL. When large number of certificates are generated with long
|
|
|
|
lifetimes, it is recommended that lease generation be disabled, as large amount of
|
|
|
|
leases adversely affect the startup time of Vault.`,
|
|
|
|
},
|
2017-04-07 18:25:47 +00:00
|
|
|
"no_store": &framework.FieldSchema{
|
|
|
|
Type: framework.TypeBool,
|
|
|
|
Default: false,
|
|
|
|
Description: `
|
|
|
|
If set, certificates issued/signed against this role will not be stored in the
|
2017-12-07 15:40:21 +00:00
|
|
|
storage backend. This can improve performance when issuing large numbers of
|
|
|
|
certificates. However, certificates issued in this way cannot be enumerated
|
2017-04-07 18:25:47 +00:00
|
|
|
or revoked, so this option is recommended only for certificates that are
|
|
|
|
non-sensitive, or extremely short-lived. This option implies a value of "false"
|
|
|
|
for "generate_lease".`,
|
|
|
|
},
|
2018-02-09 18:42:19 +00:00
|
|
|
"require_cn": &framework.FieldSchema{
|
|
|
|
Type: framework.TypeBool,
|
|
|
|
Default: true,
|
|
|
|
Description: `If set to false, makes the 'common_name' field optional while generating a certificate.`,
|
|
|
|
},
|
2015-05-15 16:13:05 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
|
|
|
logical.ReadOperation: b.pathRoleRead,
|
2016-01-28 20:18:07 +00:00
|
|
|
logical.UpdateOperation: b.pathRoleCreate,
|
2015-05-15 16:13:05 +00:00
|
|
|
logical.DeleteOperation: b.pathRoleDelete,
|
|
|
|
},
|
|
|
|
|
|
|
|
HelpSynopsis: pathRoleHelpSyn,
|
|
|
|
HelpDescription: pathRoleHelpDesc,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-19 06:44:44 +00:00
|
|
|
func (b *backend) getRole(ctx context.Context, s logical.Storage, n string) (*roleEntry, error) {
|
|
|
|
entry, err := s.Get(ctx, "role/"+n)
|
2015-05-15 16:13:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if entry == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var result roleEntry
|
|
|
|
if err := entry.DecodeJSON(&result); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2015-08-27 19:24:37 +00:00
|
|
|
// Migrate existing saved entries and save back if changed
|
|
|
|
modified := false
|
|
|
|
if len(result.TTL) == 0 && len(result.Lease) != 0 {
|
|
|
|
result.TTL = result.Lease
|
|
|
|
result.Lease = ""
|
|
|
|
modified = true
|
|
|
|
}
|
|
|
|
if len(result.MaxTTL) == 0 && len(result.LeaseMax) != 0 {
|
|
|
|
result.MaxTTL = result.LeaseMax
|
|
|
|
result.LeaseMax = ""
|
|
|
|
modified = true
|
|
|
|
}
|
2015-12-01 04:49:11 +00:00
|
|
|
if result.AllowBaseDomain {
|
|
|
|
result.AllowBaseDomain = false
|
|
|
|
result.AllowBareDomains = true
|
|
|
|
modified = true
|
|
|
|
}
|
2017-12-11 18:13:35 +00:00
|
|
|
if result.AllowedDomainsOld != "" {
|
|
|
|
result.AllowedDomains = strings.Split(result.AllowedDomainsOld, ",")
|
|
|
|
result.AllowedDomainsOld = ""
|
|
|
|
modified = true
|
|
|
|
}
|
2015-12-01 04:49:11 +00:00
|
|
|
if result.AllowedBaseDomain != "" {
|
|
|
|
found := false
|
2017-12-11 18:13:35 +00:00
|
|
|
for _, v := range result.AllowedDomains {
|
|
|
|
if v == result.AllowedBaseDomain {
|
|
|
|
found = true
|
|
|
|
break
|
2015-12-01 04:49:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found {
|
2017-12-11 18:13:35 +00:00
|
|
|
result.AllowedDomains = append(result.AllowedDomains, result.AllowedBaseDomain)
|
2015-12-01 04:49:11 +00:00
|
|
|
}
|
2016-02-09 21:42:15 +00:00
|
|
|
result.AllowedBaseDomain = ""
|
|
|
|
modified = true
|
2015-12-01 04:49:11 +00:00
|
|
|
}
|
|
|
|
|
2017-02-24 17:12:40 +00:00
|
|
|
// Upgrade generate_lease in role
|
|
|
|
if result.GenerateLease == nil {
|
|
|
|
// All the new roles will have GenerateLease always set to a value. A
|
|
|
|
// nil value indicates that this role needs an upgrade. Set it to
|
|
|
|
// `true` to not alter its current behavior.
|
|
|
|
result.GenerateLease = new(bool)
|
|
|
|
*result.GenerateLease = true
|
|
|
|
modified = true
|
|
|
|
}
|
|
|
|
|
2017-12-11 18:13:35 +00:00
|
|
|
// Upgrade key usages
|
|
|
|
if result.KeyUsageOld != "" {
|
|
|
|
result.KeyUsage = strings.Split(result.KeyUsageOld, ",")
|
|
|
|
result.KeyUsageOld = ""
|
|
|
|
modified = true
|
|
|
|
}
|
|
|
|
|
2018-01-17 16:53:49 +00:00
|
|
|
// Upgrade OU
|
|
|
|
if result.OUOld != "" {
|
|
|
|
result.OU = strings.Split(result.OUOld, ",")
|
|
|
|
result.OUOld = ""
|
|
|
|
modified = true
|
|
|
|
}
|
|
|
|
|
|
|
|
// Upgrade Organization
|
|
|
|
if result.OrganizationOld != "" {
|
|
|
|
result.Organization = strings.Split(result.OrganizationOld, ",")
|
|
|
|
result.OrganizationOld = ""
|
|
|
|
modified = true
|
|
|
|
}
|
|
|
|
|
2018-02-03 01:28:25 +00:00
|
|
|
if modified && (b.System().LocalMount() || !b.System().ReplicationState().HasState(consts.ReplicationPerformanceSecondary)) {
|
2015-08-27 19:24:37 +00:00
|
|
|
jsonEntry, err := logical.StorageEntryJSON("role/"+n, &result)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-01-19 06:44:44 +00:00
|
|
|
if err := s.Put(ctx, jsonEntry); err != nil {
|
2017-12-11 18:13:35 +00:00
|
|
|
// Only perform upgrades on replication primary
|
|
|
|
if !strings.Contains(err.Error(), logical.ErrReadOnly.Error()) {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-08-27 19:24:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-15 16:13:05 +00:00
|
|
|
return &result, nil
|
|
|
|
}
|
|
|
|
|
2018-01-08 18:31:38 +00:00
|
|
|
func (b *backend) pathRoleDelete(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
2018-01-19 06:44:44 +00:00
|
|
|
err := req.Storage.Delete(ctx, "role/"+data.Get("name").(string))
|
2015-05-15 16:13:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2018-01-08 18:31:38 +00:00
|
|
|
func (b *backend) pathRoleRead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
2017-02-24 17:12:40 +00:00
|
|
|
roleName := data.Get("name").(string)
|
|
|
|
if roleName == "" {
|
|
|
|
return logical.ErrorResponse("missing role name"), nil
|
|
|
|
}
|
|
|
|
|
2018-01-19 06:44:44 +00:00
|
|
|
role, err := b.getRole(ctx, req.Storage, roleName)
|
2015-05-15 16:13:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if role == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2015-08-27 19:24:37 +00:00
|
|
|
hasMax := true
|
|
|
|
if len(role.MaxTTL) == 0 {
|
|
|
|
role.MaxTTL = "(system default)"
|
|
|
|
hasMax = false
|
|
|
|
}
|
|
|
|
if len(role.TTL) == 0 {
|
|
|
|
if hasMax {
|
|
|
|
role.TTL = "(system default, capped to role max)"
|
|
|
|
} else {
|
|
|
|
role.TTL = "(system default)"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-15 16:13:05 +00:00
|
|
|
resp := &logical.Response{
|
2017-12-11 18:13:35 +00:00
|
|
|
Data: role.ToResponseData(),
|
2016-05-26 14:32:04 +00:00
|
|
|
}
|
2015-05-15 16:13:05 +00:00
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
2018-01-08 18:31:38 +00:00
|
|
|
func (b *backend) pathRoleList(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
2018-01-19 06:44:44 +00:00
|
|
|
entries, err := req.Storage.List(ctx, "role/")
|
2016-01-28 20:18:07 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return logical.ListResponse(entries), nil
|
|
|
|
}
|
|
|
|
|
2018-01-08 18:31:38 +00:00
|
|
|
func (b *backend) pathRoleCreate(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
2015-08-27 19:24:37 +00:00
|
|
|
var err error
|
2015-05-15 16:13:05 +00:00
|
|
|
name := data.Get("name").(string)
|
|
|
|
|
|
|
|
entry := &roleEntry{
|
2015-11-30 18:41:16 +00:00
|
|
|
MaxTTL: data.Get("max_ttl").(string),
|
2017-08-31 19:46:13 +00:00
|
|
|
TTL: (time.Duration(data.Get("ttl").(int)) * time.Second).String(),
|
2015-11-30 18:41:16 +00:00
|
|
|
AllowLocalhost: data.Get("allow_localhost").(bool),
|
2017-12-11 18:13:35 +00:00
|
|
|
AllowedDomains: data.Get("allowed_domains").([]string),
|
2015-12-01 04:49:11 +00:00
|
|
|
AllowBareDomains: data.Get("allow_bare_domains").(bool),
|
2015-11-30 18:41:16 +00:00
|
|
|
AllowSubdomains: data.Get("allow_subdomains").(bool),
|
2017-05-01 14:40:18 +00:00
|
|
|
AllowGlobDomains: data.Get("allow_glob_domains").(bool),
|
2015-11-30 18:41:16 +00:00
|
|
|
AllowAnyName: data.Get("allow_any_name").(bool),
|
|
|
|
EnforceHostnames: data.Get("enforce_hostnames").(bool),
|
|
|
|
AllowIPSANs: data.Get("allow_ip_sans").(bool),
|
|
|
|
ServerFlag: data.Get("server_flag").(bool),
|
|
|
|
ClientFlag: data.Get("client_flag").(bool),
|
|
|
|
CodeSigningFlag: data.Get("code_signing_flag").(bool),
|
|
|
|
EmailProtectionFlag: data.Get("email_protection_flag").(bool),
|
|
|
|
KeyType: data.Get("key_type").(string),
|
|
|
|
KeyBits: data.Get("key_bits").(int),
|
|
|
|
UseCSRCommonName: data.Get("use_csr_common_name").(bool),
|
2017-03-15 18:38:18 +00:00
|
|
|
UseCSRSANs: data.Get("use_csr_sans").(bool),
|
2017-12-11 18:13:35 +00:00
|
|
|
KeyUsage: data.Get("key_usage").([]string),
|
2018-01-17 16:53:49 +00:00
|
|
|
OU: data.Get("ou").([]string),
|
|
|
|
Organization: data.Get("organization").([]string),
|
2017-02-24 17:12:40 +00:00
|
|
|
GenerateLease: new(bool),
|
2017-04-07 18:25:47 +00:00
|
|
|
NoStore: data.Get("no_store").(bool),
|
2018-02-09 18:42:19 +00:00
|
|
|
RequireCN: data.Get("require_cn").(bool),
|
2015-05-15 16:13:05 +00:00
|
|
|
}
|
|
|
|
|
2017-04-07 18:25:47 +00:00
|
|
|
// no_store implies generate_lease := false
|
|
|
|
if entry.NoStore {
|
|
|
|
*entry.GenerateLease = false
|
|
|
|
} else {
|
|
|
|
*entry.GenerateLease = data.Get("generate_lease").(bool)
|
|
|
|
}
|
2017-02-24 17:12:40 +00:00
|
|
|
|
2016-02-18 22:55:47 +00:00
|
|
|
if entry.KeyType == "rsa" && entry.KeyBits < 2048 {
|
|
|
|
return logical.ErrorResponse("RSA keys < 2048 bits are unsafe and not supported"), nil
|
|
|
|
}
|
|
|
|
|
2015-08-27 19:24:37 +00:00
|
|
|
var maxTTL time.Duration
|
2015-09-10 19:09:34 +00:00
|
|
|
maxSystemTTL := b.System().MaxLeaseTTL()
|
2015-08-27 19:24:37 +00:00
|
|
|
if len(entry.MaxTTL) == 0 {
|
2015-09-02 19:56:58 +00:00
|
|
|
maxTTL = maxSystemTTL
|
2015-08-27 19:24:37 +00:00
|
|
|
} else {
|
2017-03-07 16:21:22 +00:00
|
|
|
maxTTL, err = parseutil.ParseDurationSecond(entry.MaxTTL)
|
2015-08-27 19:24:37 +00:00
|
|
|
if err != nil {
|
|
|
|
return logical.ErrorResponse(fmt.Sprintf(
|
2017-03-02 20:38:34 +00:00
|
|
|
"Invalid max ttl: %s", err)), nil
|
2015-08-27 19:24:37 +00:00
|
|
|
}
|
|
|
|
}
|
2015-09-02 19:56:58 +00:00
|
|
|
if maxTTL > maxSystemTTL {
|
2015-09-10 19:09:34 +00:00
|
|
|
return logical.ErrorResponse("Requested max TTL is higher than backend maximum"), nil
|
2015-05-15 16:13:05 +00:00
|
|
|
}
|
|
|
|
|
2015-09-10 19:09:34 +00:00
|
|
|
ttl := b.System().DefaultLeaseTTL()
|
2015-09-02 19:56:58 +00:00
|
|
|
if len(entry.TTL) != 0 {
|
2017-03-07 16:21:22 +00:00
|
|
|
ttl, err = parseutil.ParseDurationSecond(entry.TTL)
|
2015-05-15 16:13:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return logical.ErrorResponse(fmt.Sprintf(
|
2015-08-27 19:24:37 +00:00
|
|
|
"Invalid ttl: %s", err)), nil
|
2015-05-15 16:13:05 +00:00
|
|
|
}
|
2015-08-27 19:24:37 +00:00
|
|
|
}
|
|
|
|
if ttl > maxTTL {
|
|
|
|
// If they are using the system default, cap it to the role max;
|
|
|
|
// if it was specified on the command line, make it an error
|
|
|
|
if len(entry.TTL) == 0 {
|
2016-05-15 15:13:56 +00:00
|
|
|
ttl = maxTTL
|
2015-08-27 19:24:37 +00:00
|
|
|
} else {
|
2015-10-05 17:00:45 +00:00
|
|
|
return logical.ErrorResponse(
|
|
|
|
`"ttl" value must be less than "max_ttl" and/or backend default max lease TTL value`,
|
|
|
|
), nil
|
2015-05-15 16:13:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-15 15:13:56 +00:00
|
|
|
// Persist clamped TTLs
|
|
|
|
entry.TTL = ttl.String()
|
|
|
|
entry.MaxTTL = maxTTL.String()
|
|
|
|
|
2015-11-16 21:32:49 +00:00
|
|
|
if errResp := validateKeyTypeLength(entry.KeyType, entry.KeyBits); errResp != nil {
|
|
|
|
return errResp, nil
|
2015-05-15 16:13:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Store it
|
|
|
|
jsonEntry, err := logical.StorageEntryJSON("role/"+name, entry)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-01-19 06:44:44 +00:00
|
|
|
if err := req.Storage.Put(ctx, jsonEntry); err != nil {
|
2015-05-15 16:13:05 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2017-12-11 18:13:35 +00:00
|
|
|
func parseKeyUsages(input []string) int {
|
2016-06-22 20:08:24 +00:00
|
|
|
var parsedKeyUsages x509.KeyUsage
|
2017-12-11 18:13:35 +00:00
|
|
|
for _, k := range input {
|
2016-06-23 14:18:03 +00:00
|
|
|
switch strings.ToLower(strings.TrimSpace(k)) {
|
2016-06-22 20:08:24 +00:00
|
|
|
case "digitalsignature":
|
2016-06-23 14:18:03 +00:00
|
|
|
parsedKeyUsages |= x509.KeyUsageDigitalSignature
|
2016-06-22 20:08:24 +00:00
|
|
|
case "contentcommitment":
|
2016-06-23 14:18:03 +00:00
|
|
|
parsedKeyUsages |= x509.KeyUsageContentCommitment
|
2016-06-22 20:08:24 +00:00
|
|
|
case "keyencipherment":
|
2016-06-23 14:18:03 +00:00
|
|
|
parsedKeyUsages |= x509.KeyUsageKeyEncipherment
|
2016-06-22 20:08:24 +00:00
|
|
|
case "dataencipherment":
|
2016-06-23 14:18:03 +00:00
|
|
|
parsedKeyUsages |= x509.KeyUsageDataEncipherment
|
2016-06-22 20:08:24 +00:00
|
|
|
case "keyagreement":
|
2016-06-23 14:18:03 +00:00
|
|
|
parsedKeyUsages |= x509.KeyUsageKeyAgreement
|
2016-06-22 20:08:24 +00:00
|
|
|
case "certsign":
|
2016-06-23 14:18:03 +00:00
|
|
|
parsedKeyUsages |= x509.KeyUsageCertSign
|
2016-06-22 20:08:24 +00:00
|
|
|
case "crlsign":
|
2016-06-23 14:18:03 +00:00
|
|
|
parsedKeyUsages |= x509.KeyUsageCRLSign
|
2016-06-22 20:08:24 +00:00
|
|
|
case "encipheronly":
|
2016-06-23 14:18:03 +00:00
|
|
|
parsedKeyUsages |= x509.KeyUsageEncipherOnly
|
2016-06-22 20:08:24 +00:00
|
|
|
case "decipheronly":
|
2016-06-23 14:18:03 +00:00
|
|
|
parsedKeyUsages |= x509.KeyUsageDecipherOnly
|
2016-06-22 20:08:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return int(parsedKeyUsages)
|
|
|
|
}
|
|
|
|
|
2015-05-15 16:13:05 +00:00
|
|
|
type roleEntry struct {
|
2017-12-11 18:13:35 +00:00
|
|
|
LeaseMax string `json:"lease_max"`
|
|
|
|
Lease string `json:"lease"`
|
|
|
|
MaxTTL string `json:"max_ttl" mapstructure:"max_ttl"`
|
|
|
|
TTL string `json:"ttl" mapstructure:"ttl"`
|
|
|
|
AllowLocalhost bool `json:"allow_localhost" mapstructure:"allow_localhost"`
|
|
|
|
AllowedBaseDomain string `json:"allowed_base_domain" mapstructure:"allowed_base_domain"`
|
|
|
|
AllowedDomainsOld string `json:"allowed_domains,omit_empty"`
|
|
|
|
AllowedDomains []string `json:"allowed_domains_list" mapstructure:"allowed_domains"`
|
|
|
|
AllowBaseDomain bool `json:"allow_base_domain"`
|
|
|
|
AllowBareDomains bool `json:"allow_bare_domains" mapstructure:"allow_bare_domains"`
|
|
|
|
AllowTokenDisplayName bool `json:"allow_token_displayname" mapstructure:"allow_token_displayname"`
|
|
|
|
AllowSubdomains bool `json:"allow_subdomains" mapstructure:"allow_subdomains"`
|
|
|
|
AllowGlobDomains bool `json:"allow_glob_domains" mapstructure:"allow_glob_domains"`
|
|
|
|
AllowAnyName bool `json:"allow_any_name" mapstructure:"allow_any_name"`
|
|
|
|
EnforceHostnames bool `json:"enforce_hostnames" mapstructure:"enforce_hostnames"`
|
|
|
|
AllowIPSANs bool `json:"allow_ip_sans" mapstructure:"allow_ip_sans"`
|
|
|
|
ServerFlag bool `json:"server_flag" mapstructure:"server_flag"`
|
|
|
|
ClientFlag bool `json:"client_flag" mapstructure:"client_flag"`
|
|
|
|
CodeSigningFlag bool `json:"code_signing_flag" mapstructure:"code_signing_flag"`
|
|
|
|
EmailProtectionFlag bool `json:"email_protection_flag" mapstructure:"email_protection_flag"`
|
|
|
|
UseCSRCommonName bool `json:"use_csr_common_name" mapstructure:"use_csr_common_name"`
|
|
|
|
UseCSRSANs bool `json:"use_csr_sans" mapstructure:"use_csr_sans"`
|
|
|
|
KeyType string `json:"key_type" mapstructure:"key_type"`
|
|
|
|
KeyBits int `json:"key_bits" mapstructure:"key_bits"`
|
|
|
|
MaxPathLength *int `json:",omitempty" mapstructure:"max_path_length"`
|
|
|
|
KeyUsageOld string `json:"key_usage,omitempty"`
|
|
|
|
KeyUsage []string `json:"key_usage_list" mapstructure:"key_usage"`
|
2018-01-17 16:53:49 +00:00
|
|
|
OUOld string `json:"ou,omitempty"`
|
|
|
|
OU []string `json:"ou_list" mapstructure:"ou"`
|
|
|
|
OrganizationOld string `json:"organization,omitempty"`
|
|
|
|
Organization []string `json:"organization_list" mapstructure:"organization"`
|
2017-12-11 18:13:35 +00:00
|
|
|
GenerateLease *bool `json:"generate_lease,omitempty"`
|
|
|
|
NoStore bool `json:"no_store" mapstructure:"no_store"`
|
2018-02-09 18:42:19 +00:00
|
|
|
RequireCN bool `json:"require_cn" mapstructure:"require_cn"`
|
2017-09-13 15:42:45 +00:00
|
|
|
|
|
|
|
// Used internally for signing intermediates
|
|
|
|
AllowExpirationPastCA bool
|
2015-05-15 16:13:05 +00:00
|
|
|
}
|
|
|
|
|
2017-12-11 18:13:35 +00:00
|
|
|
func (r *roleEntry) ToResponseData() map[string]interface{} {
|
|
|
|
responseData := map[string]interface{}{
|
|
|
|
"ttl": r.TTL,
|
|
|
|
"max_ttl": r.MaxTTL,
|
|
|
|
"allow_localhost": r.AllowLocalhost,
|
|
|
|
"allowed_domains": r.AllowedDomains,
|
|
|
|
"allow_bare_domains": r.AllowBareDomains,
|
|
|
|
"allow_token_displayname": r.AllowTokenDisplayName,
|
|
|
|
"allow_subdomains": r.AllowSubdomains,
|
|
|
|
"allow_glob_domains": r.AllowGlobDomains,
|
|
|
|
"allow_any_name": r.AllowAnyName,
|
|
|
|
"enforce_hostnames": r.EnforceHostnames,
|
|
|
|
"allow_ip_sans": r.AllowIPSANs,
|
|
|
|
"server_flag": r.ServerFlag,
|
|
|
|
"client_flag": r.ClientFlag,
|
|
|
|
"code_signing_flag": r.CodeSigningFlag,
|
|
|
|
"email_protection_flag": r.EmailProtectionFlag,
|
|
|
|
"use_csr_common_name": r.UseCSRCommonName,
|
|
|
|
"use_csr_sans": r.UseCSRSANs,
|
|
|
|
"key_type": r.KeyType,
|
|
|
|
"key_bits": r.KeyBits,
|
|
|
|
"key_usage": r.KeyUsage,
|
|
|
|
"ou": r.OU,
|
|
|
|
"organization": r.Organization,
|
|
|
|
"no_store": r.NoStore,
|
|
|
|
}
|
|
|
|
if r.MaxPathLength != nil {
|
|
|
|
responseData["max_path_length"] = r.MaxPathLength
|
|
|
|
}
|
|
|
|
if r.GenerateLease != nil {
|
|
|
|
responseData["generate_lease"] = r.GenerateLease
|
|
|
|
}
|
|
|
|
return responseData
|
|
|
|
}
|
|
|
|
|
2016-06-20 23:51:04 +00:00
|
|
|
const pathListRolesHelpSyn = `List the existing roles in this backend`
|
|
|
|
|
|
|
|
const pathListRolesHelpDesc = `Roles will be listed by the role name.`
|
|
|
|
|
|
|
|
const pathRoleHelpSyn = `Manage the roles that can be created with this backend.`
|
2015-05-15 16:13:05 +00:00
|
|
|
|
2016-06-20 23:51:04 +00:00
|
|
|
const pathRoleHelpDesc = `This path lets you manage the roles that can be created with this backend.`
|