// pathConfigCertificateCreateUpdate is used to register an AWS Public Key that is // used to verify the PKCS#7 signature of the instance identity document. func (b *backend) pathConfigCertificateCreateUpdate( req *logical.Request, data *framework.FieldData) (*logical.Response, error) { certName := data.Get("cert_name").(string) if certName == "" { return logical.ErrorResponse("missing cert_name"), nil } b.configMutex.Lock() defer b.configMutex.Unlock() // Check if there is already a certificate entry registered. certEntry, err := b.nonLockedAWSPublicCertificateEntry(req.Storage, certName) if err != nil { return nil, err } if certEntry == nil { certEntry = &awsPublicCert{} } // Check if the value is provided by the client. certStrData, ok := data.GetOk("aws_public_cert") if ok { if certBytes, err := base64.StdEncoding.DecodeString(certStrData.(string)); err == nil { certEntry.AWSPublicCert = string(certBytes) } else { certEntry.AWSPublicCert = certStrData.(string) } } else { // aws_public_cert should be supplied for both create and update operations. // If it is not provided, throw an error. return logical.ErrorResponse("missing aws_public_cert"), nil } // If explicitly set to empty string, error out. if certEntry.AWSPublicCert == "" { return logical.ErrorResponse("invalid aws_public_cert"), nil } // Verify the certificate by decoding it and parsing it. publicCert, err := decodePEMAndParseCertificate(certEntry.AWSPublicCert) if err != nil { return nil, err } if publicCert == nil { return logical.ErrorResponse("invalid certificate; failed to decode and parse certificate"), nil } // Ensure that we have not // If none of the checks fail, save the provided certificate. entry, err := logical.StorageEntryJSON("config/certificate/"+certName, certEntry) if err != nil { return nil, err } if err := req.Storage.Put(entry); err != nil { return nil, err } return nil, nil }
func (b *backend) pathConfigTidyRoletagBlacklistCreateUpdate(req *logical.Request, data *framework.FieldData) (*logical.Response, error) { b.configMutex.Lock() defer b.configMutex.Unlock() configEntry, err := b.nonLockedConfigTidyRoleTags(req.Storage) if err != nil { return nil, err } if configEntry == nil { configEntry = &tidyBlacklistRoleTagConfig{} } safetyBufferInt, ok := data.GetOk("safety_buffer") if ok { configEntry.SafetyBuffer = safetyBufferInt.(int) } else if req.Operation == logical.CreateOperation { configEntry.SafetyBuffer = data.Get("safety_buffer").(int) } disablePeriodicTidyBool, ok := data.GetOk("disable_periodic_tidy") if ok { configEntry.DisablePeriodicTidy = disablePeriodicTidyBool.(bool) } else if req.Operation == logical.CreateOperation { configEntry.DisablePeriodicTidy = data.Get("disable_periodic_tidy").(bool) } entry, err := logical.StorageEntryJSON(roletagBlacklistConfigPath, configEntry) if err != nil { return nil, err } if err := req.Storage.Put(entry); err != nil { return nil, err } return nil, nil }
// handleMountTune is used to set config settings on a backend func (b *SystemBackend) handleMountTune( req *logical.Request, data *framework.FieldData) (*logical.Response, error) { path := data.Get("path").(string) if path == "" { return logical.ErrorResponse( "path must be specified as a string"), logical.ErrInvalidRequest } if !strings.HasSuffix(path, "/") { path += "/" } // Prevent protected paths from being changed for _, p := range untunableMounts { if strings.HasPrefix(path, p) { err := fmt.Errorf("[ERR] core: cannot tune '%s'", path) b.Backend.Logger().Print(err) return handleError(err) } } mountEntry := b.Core.router.MatchingMountEntry(path) if mountEntry == nil { err := fmt.Errorf("[ERR] sys: tune of path '%s' failed: no mount entry found", path) b.Backend.Logger().Print(err) return handleError(err) } newMountConfig := mountEntry.Config // Timing configuration parameters { var needTTLTune bool defTTLInt, ok := data.GetOk("default_lease_ttl") if ok { newMountConfig.DefaultLeaseTTL = time.Duration(defTTLInt.(int)) needTTLTune = true } maxTTLInt, ok := data.GetOk("max_lease_ttl") if ok { newMountConfig.MaxLeaseTTL = time.Duration(maxTTLInt.(int)) needTTLTune = true } if needTTLTune { if err := b.tuneMountTTLs(path, &mountEntry.Config, &newMountConfig); err != nil { b.Backend.Logger().Printf("[ERR] sys: tune of path '%s' failed: %v", path, err) return handleError(err) } } } return nil, nil }
func (b *backend) pathConfigWrite( req *logical.Request, data *framework.FieldData) (*logical.Response, error) { organization := data.Get("organization").(string) baseURL := data.Get("base_url").(string) if len(baseURL) != 0 { _, err := url.Parse(baseURL) if err != nil { return logical.ErrorResponse(fmt.Sprintf("Error parsing given base_url: %s", err)), nil } } var ttl time.Duration var err error ttlRaw, ok := data.GetOk("ttl") if !ok || len(ttlRaw.(string)) == 0 { ttl = 0 } else { ttl, err = time.ParseDuration(ttlRaw.(string)) if err != nil { return logical.ErrorResponse(fmt.Sprintf("Invalid 'ttl':%s", err)), nil } } var maxTTL time.Duration maxTTLRaw, ok := data.GetOk("max_ttl") if !ok || len(maxTTLRaw.(string)) == 0 { maxTTL = 0 } else { maxTTL, err = time.ParseDuration(maxTTLRaw.(string)) if err != nil { return logical.ErrorResponse(fmt.Sprintf("Invalid 'max_ttl':%s", err)), nil } } entry, err := logical.StorageEntryJSON("config", config{ Org: organization, BaseURL: baseURL, TTL: ttl, MaxTTL: maxTTL, }) if err != nil { return nil, err } if err := req.Storage.Put(entry); err != nil { return nil, err } return nil, nil }
func pathConfigWrite( req *logical.Request, d *framework.FieldData) (*logical.Response, error) { name := d.Get("name").(string) // Check if the policy already exists policy, err := getPolicy(req, name) if err != nil { return nil, err } if policy == nil { return logical.ErrorResponse( fmt.Sprintf("no existing role named %s could be found", name)), logical.ErrInvalidRequest } persistNeeded := false minDecryptionVersion := d.Get("min_decryption_version").(int) if minDecryptionVersion != 0 && minDecryptionVersion != policy.MinDecryptionVersion { policy.MinDecryptionVersion = minDecryptionVersion persistNeeded = true } allowDeletionInt, ok := d.GetOk("deletion_allowed") if ok { allowDeletion := allowDeletionInt.(bool) if allowDeletion != policy.DeletionAllowed { policy.DeletionAllowed = allowDeletion persistNeeded = true } } if !persistNeeded { return nil, nil } return nil, policy.Persist(req.Storage, name) }
func (b *backend) userCreateUpdate(req *logical.Request, d *framework.FieldData) (*logical.Response, error) { username := strings.ToLower(d.Get("username").(string)) userEntry, err := b.user(req.Storage, username) if err != nil { return nil, err } // Due to existence check, user will only be nil if it's a create operation if userEntry == nil { userEntry = &UserEntry{} } if _, ok := d.GetOk("password"); ok { userErr, intErr := b.updateUserPassword(req, d, userEntry) if intErr != nil { return nil, err } if userErr != nil { return logical.ErrorResponse(userErr.Error()), logical.ErrInvalidRequest } } if policiesRaw, ok := d.GetOk("policies"); ok { userEntry.Policies = policyutil.ParsePolicies(policiesRaw.(string)) } ttlStr := userEntry.TTL.String() if ttlStrRaw, ok := d.GetOk("ttl"); ok { ttlStr = ttlStrRaw.(string) } maxTTLStr := userEntry.MaxTTL.String() if maxTTLStrRaw, ok := d.GetOk("max_ttl"); ok { maxTTLStr = maxTTLStrRaw.(string) } userEntry.TTL, userEntry.MaxTTL, err = b.SanitizeTTLStr(ttlStr, maxTTLStr) if err != nil { return logical.ErrorResponse(fmt.Sprintf("err: %s", err)), nil } return nil, b.setUser(req.Storage, username, userEntry) }
func (b *backend) pathWriteURL( req *logical.Request, data *framework.FieldData) (*logical.Response, error) { entries, err := getURLs(req) if err != nil { return nil, err } if entries == nil { entries = &urlEntries{ IssuingCertificates: []string{}, CRLDistributionPoints: []string{}, OCSPServers: []string{}, } } if urlsInt, ok := data.GetOk("issuing_certificates"); ok { splitURLs := strings.Split(urlsInt.(string), ",") entries.IssuingCertificates = splitURLs if badUrl := validateURLs(entries.IssuingCertificates); badUrl != "" { return logical.ErrorResponse(fmt.Sprintf( "invalid URL found in issuing certificates: %s", badUrl)), nil } } if urlsInt, ok := data.GetOk("crl_distribution_points"); ok { splitURLs := strings.Split(urlsInt.(string), ",") entries.CRLDistributionPoints = splitURLs if badUrl := validateURLs(entries.CRLDistributionPoints); badUrl != "" { return logical.ErrorResponse(fmt.Sprintf( "invalid URL found in CRL distribution points: %s", badUrl)), nil } } if urlsInt, ok := data.GetOk("ocsp_servers"); ok { splitURLs := strings.Split(urlsInt.(string), ",") entries.OCSPServers = splitURLs if badUrl := validateURLs(entries.OCSPServers); badUrl != "" { return logical.ErrorResponse(fmt.Sprintf( "invalid URL found in OCSP servers: %s", badUrl)), nil } } return nil, writeURLs(req, entries) }
func (b *backend) pathEncryptWrite( req *logical.Request, d *framework.FieldData) (*logical.Response, error) { name := d.Get("name").(string) valueRaw, ok := d.GetOk("plaintext") if !ok { return logical.ErrorResponse("missing plaintext to encrypt"), logical.ErrInvalidRequest } value := valueRaw.(string) // Decode the context if any contextRaw := d.Get("context").(string) var context []byte var err error if len(contextRaw) != 0 { context, err = base64.StdEncoding.DecodeString(contextRaw) if err != nil { return logical.ErrorResponse("failed to base64-decode context"), logical.ErrInvalidRequest } } // Decode the nonce if any nonceRaw := d.Get("nonce").(string) var nonce []byte if len(nonceRaw) != 0 { nonce, err = base64.StdEncoding.DecodeString(nonceRaw) if err != nil { return logical.ErrorResponse("failed to base64-decode nonce"), logical.ErrInvalidRequest } } // Get the policy var p *policy var lock *sync.RWMutex var upserted bool if req.Operation == logical.CreateOperation { convergent := d.Get("convergent_encryption").(bool) if convergent && len(context) == 0 { return logical.ErrorResponse("convergent encryption requires derivation to be enabled, so context is required"), nil } polReq := policyRequest{ storage: req.Storage, name: name, derived: len(context) != 0, convergent: convergent, } keyType := d.Get("type").(string) switch keyType { case "aes256-gcm96": polReq.keyType = keyType_AES256_GCM96 case "ecdsa-p256": return logical.ErrorResponse(fmt.Sprintf("key type %v not supported for this operation", keyType)), logical.ErrInvalidRequest default: return logical.ErrorResponse(fmt.Sprintf("unknown key type %v", keyType)), logical.ErrInvalidRequest } p, lock, upserted, err = b.lm.GetPolicyUpsert(polReq) } else { p, lock, err = b.lm.GetPolicyShared(req.Storage, name) } if lock != nil { defer lock.RUnlock() } if err != nil { return nil, err } if p == nil { return logical.ErrorResponse("policy not found"), logical.ErrInvalidRequest } ciphertext, err := p.Encrypt(context, nonce, value) if err != nil { switch err.(type) { case errutil.UserError: return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest case errutil.InternalError: return nil, err default: return nil, err } } if ciphertext == "" { return nil, fmt.Errorf("empty ciphertext returned") } // Generate the response resp := &logical.Response{ Data: map[string]interface{}{ "ciphertext": ciphertext, }, } if req.Operation == logical.CreateOperation && !upserted { resp.AddWarning("Attempted creation of the key during the encrypt operation, but it was created beforehand") } return resp, nil }
// pathConfigClientCreateUpdate is used to register the 'aws_secret_key' and 'aws_access_key' // that can be used to interact with AWS EC2 API. func (b *backend) pathConfigClientCreateUpdate( req *logical.Request, data *framework.FieldData) (*logical.Response, error) { b.configMutex.Lock() defer b.configMutex.Unlock() configEntry, err := b.nonLockedClientConfigEntry(req.Storage) if err != nil { return nil, err } if configEntry == nil { configEntry = &clientConfig{} } changedCreds := false accessKeyStr, ok := data.GetOk("access_key") if ok { if configEntry.AccessKey != accessKeyStr.(string) { changedCreds = true configEntry.AccessKey = accessKeyStr.(string) } } else if req.Operation == logical.CreateOperation { // Use the default configEntry.AccessKey = data.Get("access_key").(string) } secretKeyStr, ok := data.GetOk("secret_key") if ok { if configEntry.SecretKey != secretKeyStr.(string) { changedCreds = true configEntry.SecretKey = secretKeyStr.(string) } } else if req.Operation == logical.CreateOperation { configEntry.SecretKey = data.Get("secret_key").(string) } endpointStr, ok := data.GetOk("endpoint") if ok { if configEntry.Endpoint != endpointStr.(string) { changedCreds = true configEntry.Endpoint = endpointStr.(string) } } else if req.Operation == logical.CreateOperation { configEntry.Endpoint = data.Get("endpoint").(string) } // Since this endpoint supports both create operation and update operation, // the error checks for access_key and secret_key not being set are not present. // This allows calling this endpoint multiple times to provide the values. // Hence, the readers of this endpoint should do the validation on // the validation of keys before using them. entry, err := logical.StorageEntryJSON("config/client", configEntry) if err != nil { return nil, err } if err := req.Storage.Put(entry); err != nil { return nil, err } if changedCreds { b.flushCachedEC2Clients() b.flushCachedIAMClients() } return nil, nil }
func (ts *TokenStore) tokenStoreRoleCreateUpdate( req *logical.Request, data *framework.FieldData) (*logical.Response, error) { name := data.Get("role_name").(string) if name == "" { return logical.ErrorResponse("role name cannot be empty"), nil } entry, err := ts.tokenStoreRole(name) if err != nil { return nil, err } // Due to the existence check, entry will only be nil if it's a create // operation, so just create a new one if entry == nil { entry = &tsRoleEntry{ Name: name, } } // In this series of blocks, if we do not find a user-provided value and // it's a creation operation, we call data.Get to get the appropriate // default orphanInt, ok := data.GetOk("orphan") if ok { entry.Orphan = orphanInt.(bool) } else if req.Operation == logical.CreateOperation { entry.Orphan = data.Get("orphan").(bool) } periodInt, ok := data.GetOk("period") if ok { entry.Period = time.Second * time.Duration(periodInt.(int)) } else if req.Operation == logical.CreateOperation { entry.Period = time.Second * time.Duration(data.Get("period").(int)) } pathSuffixInt, ok := data.GetOk("path_suffix") if ok { pathSuffix := pathSuffixInt.(string) if pathSuffix != "" { matched := pathSuffixSanitize.MatchString(pathSuffix) if !matched { return logical.ErrorResponse(fmt.Sprintf("given role path suffix contains invalid characters; must match %s", pathSuffixSanitize.String())), nil } entry.PathSuffix = pathSuffix } } else if req.Operation == logical.CreateOperation { entry.PathSuffix = data.Get("path_suffix").(string) } allowedPoliciesInt, ok := data.GetOk("allowed_policies") if ok { allowedPolicies := allowedPoliciesInt.(string) if allowedPolicies != "" { entry.AllowedPolicies = strings.Split(allowedPolicies, ",") } } else if req.Operation == logical.CreateOperation { entry.AllowedPolicies = strings.Split(data.Get("allowed_policies").(string), ",") } // Store it jsonEntry, err := logical.StorageEntryJSON(fmt.Sprintf("%s%s", rolesPrefix, name), entry) if err != nil { return nil, err } if err := ts.view.Put(jsonEntry); err != nil { return nil, err } return nil, nil }
// pathRoleCreateUpdate is used to associate Vault policies to a given AMI ID. func (b *backend) pathRoleCreateUpdate( req *logical.Request, data *framework.FieldData) (*logical.Response, error) { roleName := strings.ToLower(data.Get("role").(string)) if roleName == "" { return logical.ErrorResponse("missing role"), nil } b.roleMutex.Lock() defer b.roleMutex.Unlock() roleEntry, err := b.nonLockedAWSRole(req.Storage, roleName) if err != nil { return nil, err } if roleEntry == nil { roleEntry = &awsRoleEntry{} } // Fetch and set the bound parameters. There can't be default values // for these. if boundAmiIDRaw, ok := data.GetOk("bound_ami_id"); ok { roleEntry.BoundAmiID = boundAmiIDRaw.(string) } if boundAccountIDRaw, ok := data.GetOk("bound_account_id"); ok { roleEntry.BoundAccountID = boundAccountIDRaw.(string) } if boundIamRoleARNRaw, ok := data.GetOk("bound_iam_role_arn"); ok { roleEntry.BoundIamRoleARN = boundIamRoleARNRaw.(string) } if boundIamInstanceProfileARNRaw, ok := data.GetOk("bound_iam_instance_profile_arn"); ok { roleEntry.BoundIamInstanceProfileARN = boundIamInstanceProfileARNRaw.(string) } // Ensure that at least one bound is set on the role switch { case roleEntry.BoundAccountID != "": case roleEntry.BoundAmiID != "": case roleEntry.BoundIamInstanceProfileARN != "": case roleEntry.BoundIamRoleARN != "": default: return logical.ErrorResponse("at least be one bound parameter should be specified on the role"), nil } policiesStr, ok := data.GetOk("policies") if ok { roleEntry.Policies = policyutil.ParsePolicies(policiesStr.(string)) } else if req.Operation == logical.CreateOperation { roleEntry.Policies = []string{"default"} } disallowReauthenticationBool, ok := data.GetOk("disallow_reauthentication") if ok { roleEntry.DisallowReauthentication = disallowReauthenticationBool.(bool) } else if req.Operation == logical.CreateOperation { roleEntry.DisallowReauthentication = data.Get("disallow_reauthentication").(bool) } allowInstanceMigrationBool, ok := data.GetOk("allow_instance_migration") if ok { roleEntry.AllowInstanceMigration = allowInstanceMigrationBool.(bool) } else if req.Operation == logical.CreateOperation { roleEntry.AllowInstanceMigration = data.Get("allow_instance_migration").(bool) } var resp logical.Response ttlRaw, ok := data.GetOk("ttl") if ok { ttl := time.Duration(ttlRaw.(int)) * time.Second defaultLeaseTTL := b.System().DefaultLeaseTTL() if ttl > defaultLeaseTTL { resp.AddWarning(fmt.Sprintf("Given ttl of %d seconds greater than current mount/system default of %d seconds; ttl will be capped at login time", ttl/time.Second, defaultLeaseTTL/time.Second)) } roleEntry.TTL = ttl } else if req.Operation == logical.CreateOperation { roleEntry.TTL = time.Duration(data.Get("ttl").(int)) * time.Second } maxTTLInt, ok := data.GetOk("max_ttl") if ok { maxTTL := time.Duration(maxTTLInt.(int)) * time.Second systemMaxTTL := b.System().MaxLeaseTTL() if maxTTL > systemMaxTTL { resp.AddWarning(fmt.Sprintf("Given max_ttl of %d seconds greater than current mount/system default of %d seconds; max_ttl will be capped at login time", maxTTL/time.Second, systemMaxTTL/time.Second)) } if maxTTL < time.Duration(0) { return logical.ErrorResponse("max_ttl cannot be negative"), nil } roleEntry.MaxTTL = maxTTL } else if req.Operation == logical.CreateOperation { roleEntry.MaxTTL = time.Duration(data.Get("max_ttl").(int)) * time.Second } if roleEntry.MaxTTL != 0 && roleEntry.MaxTTL < roleEntry.TTL { return logical.ErrorResponse("ttl should be shorter than max_ttl"), nil } roleTagStr, ok := data.GetOk("role_tag") if ok { roleEntry.RoleTag = roleTagStr.(string) // There is a limit of 127 characters on the tag key for AWS EC2 instances. // Complying to that requirement, do not allow the value of 'key' to be more than that. if len(roleEntry.RoleTag) > 127 { return logical.ErrorResponse("length of role tag exceeds the EC2 key limit of 127 characters"), nil } } else if req.Operation == logical.CreateOperation { roleEntry.RoleTag = data.Get("role_tag").(string) } if roleEntry.HMACKey == "" { roleEntry.HMACKey, err = uuid.GenerateUUID() if err != nil { return nil, fmt.Errorf("failed to generate role HMAC key: %v", err) } } if err := b.nonLockedSetAWSRole(req.Storage, roleName, roleEntry); err != nil { return nil, err } if len(resp.Warnings()) == 0 { return nil, nil } return &resp, nil }
// pathConfigCertificateCreateUpdate is used to register an AWS Public Key that // is used to verify the PKCS#7 signature of the instance identity document. func (b *backend) pathConfigCertificateCreateUpdate( req *logical.Request, data *framework.FieldData) (*logical.Response, error) { certName := data.Get("cert_name").(string) if certName == "" { return logical.ErrorResponse("missing certificate name"), nil } b.configMutex.Lock() defer b.configMutex.Unlock() // Check if there is already a certificate entry registered certEntry, err := b.nonLockedAWSPublicCertificateEntry(req.Storage, certName) if err != nil { return nil, err } if certEntry == nil { certEntry = &awsPublicCert{} } // Check if type information is provided certTypeRaw, ok := data.GetOk("type") if ok { certEntry.Type = strings.ToLower(certTypeRaw.(string)) } else if req.Operation == logical.CreateOperation { certEntry.Type = data.Get("type").(string) } switch certEntry.Type { case "pkcs7": case "identity": default: return logical.ErrorResponse(fmt.Sprintf("invalid certificate type %q", certEntry.Type)), nil } // Check if the value is provided by the client certStrData, ok := data.GetOk("aws_public_cert") if ok { if certBytes, err := base64.StdEncoding.DecodeString(certStrData.(string)); err == nil { certEntry.AWSPublicCert = string(certBytes) } else { certEntry.AWSPublicCert = certStrData.(string) } } else { // aws_public_cert should be supplied for both create and update operations. // If it is not provided, throw an error. return logical.ErrorResponse("missing aws_public_cert"), nil } // If explicitly set to empty string, error out if certEntry.AWSPublicCert == "" { return logical.ErrorResponse("invalid aws_public_cert"), nil } // Verify the certificate by decoding it and parsing it publicCert, err := decodePEMAndParseCertificate(certEntry.AWSPublicCert) if err != nil { return nil, err } if publicCert == nil { return logical.ErrorResponse("invalid certificate; failed to decode and parse certificate"), nil } // If none of the checks fail, save the provided certificate if err := b.nonLockedSetAWSPublicCertificateEntry(req.Storage, certName, certEntry); err != nil { return nil, err } return nil, nil }
// generateCreationBundle is a shared function that reads parameters supplied // from the various endpoints and generates a creationBundle with the // parameters that can be used to issue or sign func generateCreationBundle(b *backend, role *roleEntry, signingBundle *caInfoBundle, csr *x509.CertificateRequest, req *logical.Request, data *framework.FieldData) (*creationBundle, error) { var err error var ok bool // Get the common name var cn string { if csr != nil { if role.UseCSRCommonName { cn = csr.Subject.CommonName } } if cn == "" { cn = data.Get("common_name").(string) if cn == "" { return nil, errutil.UserError{Err: `the common_name field is required, or must be provided in a CSR with "use_csr_common_name" set to true`} } } } // Read in alternate names -- DNS and email addresses dnsNames := []string{} emailAddresses := []string{} { if !data.Get("exclude_cn_from_sans").(bool) { if strings.Contains(cn, "@") { // Note: emails are not disallowed if the role's email protection // flag is false, because they may well be included for // informational purposes; it is up to the verifying party to // ensure that email addresses in a subject alternate name can be // used for the purpose for which they are presented emailAddresses = append(emailAddresses, cn) } else { dnsNames = append(dnsNames, cn) } } cnAltInt, ok := data.GetOk("alt_names") if ok { cnAlt := cnAltInt.(string) if len(cnAlt) != 0 { for _, v := range strings.Split(cnAlt, ",") { if strings.Contains(v, "@") { emailAddresses = append(emailAddresses, v) } else { dnsNames = append(dnsNames, v) } } } } // Check for bad email and/or DNS names badName, err := validateNames(req, dnsNames, role) if len(badName) != 0 { return nil, errutil.UserError{Err: fmt.Sprintf( "name %s not allowed by this role", badName)} } else if err != nil { return nil, errutil.InternalError{Err: fmt.Sprintf( "error validating name %s: %s", badName, err)} } badName, err = validateNames(req, emailAddresses, role) if len(badName) != 0 { return nil, errutil.UserError{Err: fmt.Sprintf( "email %s not allowed by this role", badName)} } else if err != nil { return nil, errutil.InternalError{Err: fmt.Sprintf( "error validating name %s: %s", badName, err)} } } // Get and verify any IP SANs ipAddresses := []net.IP{} var ipAltInt interface{} { ipAltInt, ok = data.GetOk("ip_sans") if ok { ipAlt := ipAltInt.(string) if len(ipAlt) != 0 { if !role.AllowIPSANs { return nil, errutil.UserError{Err: fmt.Sprintf( "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, errutil.UserError{Err: fmt.Sprintf( "the value '%s' is not a valid IP address", v)} } ipAddresses = append(ipAddresses, parsedIP) } } } } // Get the TTL and very it against the max allowed var ttlField string var ttl time.Duration var maxTTL time.Duration var ttlFieldInt interface{} { ttlFieldInt, ok = data.GetOk("ttl") if !ok { ttlField = role.TTL } else { ttlField = ttlFieldInt.(string) } if len(ttlField) == 0 { ttl = b.System().DefaultLeaseTTL() } else { ttl, err = time.ParseDuration(ttlField) if err != nil { return nil, errutil.UserError{Err: fmt.Sprintf( "invalid requested ttl: %s", err)} } } if len(role.MaxTTL) == 0 { maxTTL = b.System().MaxLeaseTTL() } else { maxTTL, err = time.ParseDuration(role.MaxTTL) if err != nil { return nil, errutil.UserError{Err: fmt.Sprintf( "invalid ttl: %s", err)} } } if ttl > maxTTL { // Don't error if they were using system defaults, only error if // they specifically chose a bad TTL if len(ttlField) == 0 { ttl = maxTTL } else { return nil, errutil.UserError{Err: fmt.Sprintf( "ttl is larger than maximum allowed (%d)", maxTTL/time.Second)} } } // If it's not self-signed, verify that the issued certificate won't be // valid past the lifetime of the CA certificate if signingBundle != nil && time.Now().Add(ttl).After(signingBundle.Certificate.NotAfter) { return nil, errutil.UserError{Err: fmt.Sprintf( "cannot satisfy request, as TTL is beyond the expiration of the CA certificate")} } } // Build up usages var extUsage certExtKeyUsage { if role.ServerFlag { extUsage = extUsage | serverExtKeyUsage } if role.ClientFlag { extUsage = extUsage | clientExtKeyUsage } if role.CodeSigningFlag { extUsage = extUsage | codeSigningExtKeyUsage } if role.EmailProtectionFlag { extUsage = extUsage | emailProtectionExtKeyUsage } } creationBundle := &creationBundle{ CommonName: cn, DNSNames: dnsNames, EmailAddresses: emailAddresses, IPAddresses: ipAddresses, KeyType: role.KeyType, KeyBits: role.KeyBits, SigningBundle: signingBundle, TTL: ttl, KeyUsage: x509.KeyUsage(parseKeyUsages(role.KeyUsage)), ExtKeyUsage: extUsage, } // Don't deal with URLs or max path length if it's self-signed, as these // normally come from the signing bundle if signingBundle == nil { return creationBundle, nil } // This will have been read in from the getURLs function creationBundle.URLs = signingBundle.URLs // If the max path length in the role is not nil, it was specified at // generation time with the max_path_length parameter; otherwise derive it // from the signing certificate if role.MaxPathLength != nil { creationBundle.MaxPathLength = *role.MaxPathLength } else { switch { case signingBundle.Certificate.MaxPathLen < 0: creationBundle.MaxPathLength = -1 case signingBundle.Certificate.MaxPathLen == 0 && signingBundle.Certificate.MaxPathLenZero: // The signing function will ensure that we do not issue a CA cert creationBundle.MaxPathLength = 0 default: // If this takes it to zero, we handle this case later if // necessary creationBundle.MaxPathLength = signingBundle.Certificate.MaxPathLen - 1 } } return creationBundle, nil }
func (b *backend) pathCASignIntermediate( req *logical.Request, data *framework.FieldData) (*logical.Response, error) { var err error format := getFormat(data) if format == "" { return logical.ErrorResponse( `The "format" path parameter must be "pem" or "der"`, ), nil } role := &roleEntry{ TTL: data.Get("ttl").(string), AllowLocalhost: true, AllowAnyName: true, AllowIPSANs: true, EnforceHostnames: false, KeyType: "any", } if cn := data.Get("common_name").(string); len(cn) == 0 { role.UseCSRCommonName = true } var caErr error signingBundle, caErr := fetchCAInfo(req) switch caErr.(type) { case certutil.UserError: return nil, certutil.UserError{Err: fmt.Sprintf( "could not fetch the CA certificate (was one set?): %s", caErr)} case certutil.InternalError: return nil, certutil.InternalError{Err: fmt.Sprintf( "error fetching CA certificate: %s", caErr)} } useCSRValues := data.Get("use_csr_values").(bool) maxPathLengthIface, ok := data.GetOk("max_path_length") if ok { maxPathLength := maxPathLengthIface.(int) role.MaxPathLength = &maxPathLength } parsedBundle, err := signCert(b, role, signingBundle, true, useCSRValues, req, data) if err != nil { switch err.(type) { case certutil.UserError: 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{}{ "expiration": int64(parsedBundle.Certificate.NotAfter.Unix()), "serial_number": cb.SerialNumber, }, } switch format { case "pem": resp.Data["certificate"] = cb.Certificate resp.Data["issuing_ca"] = cb.IssuingCA case "pem_bundle": resp.Data["certificate"] = fmt.Sprintf("%s\n%s", cb.Certificate, cb.IssuingCA) resp.Data["issuing_ca"] = cb.IssuingCA case "der": resp.Data["certificate"] = base64.StdEncoding.EncodeToString(parsedBundle.CertificateBytes) resp.Data["issuing_ca"] = base64.StdEncoding.EncodeToString(parsedBundle.IssuingCABytes) } err = req.Storage.Put(&logical.StorageEntry{ Key: "certs/" + cb.SerialNumber, Value: parsedBundle.CertificateBytes, }) if err != nil { return nil, fmt.Errorf("Unable to store certificate locally") } if parsedBundle.Certificate.MaxPathLen == 0 { resp.AddWarning("Max path length of the signed certificate is zero. This certificate cannot be used to issue intermediate CA certificates.") } return resp, nil }
// pathRoleCreateUpdate is used to associate Vault policies to a given AMI ID. func (b *backend) pathRoleCreateUpdate( req *logical.Request, data *framework.FieldData) (*logical.Response, error) { roleName := strings.ToLower(data.Get("role").(string)) if roleName == "" { return logical.ErrorResponse("missing role"), nil } b.roleMutex.Lock() defer b.roleMutex.Unlock() roleEntry, err := b.nonLockedAWSRole(req.Storage, roleName) if err != nil { return nil, err } if roleEntry == nil { roleEntry = &awsRoleEntry{} } // Set the bound parameters only if they are supplied. // There are no default values for bound parameters. boundAmiIDStr, ok := data.GetOk("bound_ami_id") if ok { roleEntry.BoundAmiID = boundAmiIDStr.(string) } boundIamARNStr, ok := data.GetOk("bound_iam_role_arn") if ok { roleEntry.BoundIamARN = boundIamARNStr.(string) } // At least one bound parameter should be set. Currently, only // 'bound_ami_id' and 'bound_iam_role_arn' are supported. Check if one of them is set. if roleEntry.BoundAmiID == "" { // check if an IAM Role ARN was provided instead of an AMI ID if roleEntry.BoundIamARN == "" { return logical.ErrorResponse("role is not bounded to any resource; set bound_ami_id or bount_iam_role_arn"), nil } } policiesStr, ok := data.GetOk("policies") if ok { roleEntry.Policies = policyutil.ParsePolicies(policiesStr.(string)) } else if req.Operation == logical.CreateOperation { roleEntry.Policies = []string{"default"} } disallowReauthenticationBool, ok := data.GetOk("disallow_reauthentication") if ok { roleEntry.DisallowReauthentication = disallowReauthenticationBool.(bool) } else if req.Operation == logical.CreateOperation { roleEntry.DisallowReauthentication = data.Get("disallow_reauthentication").(bool) } allowInstanceMigrationBool, ok := data.GetOk("allow_instance_migration") if ok { roleEntry.AllowInstanceMigration = allowInstanceMigrationBool.(bool) } else if req.Operation == logical.CreateOperation { roleEntry.AllowInstanceMigration = data.Get("allow_instance_migration").(bool) } var resp logical.Response maxTTLInt, ok := data.GetOk("max_ttl") if ok { maxTTL := time.Duration(maxTTLInt.(int)) * time.Second systemMaxTTL := b.System().MaxLeaseTTL() if maxTTL > systemMaxTTL { resp.AddWarning(fmt.Sprintf("Given TTL of %d seconds greater than current mount/system default of %d seconds; TTL will be capped at login time", maxTTL/time.Second, systemMaxTTL/time.Second)) } if maxTTL < time.Duration(0) { return logical.ErrorResponse("max_ttl cannot be negative"), nil } roleEntry.MaxTTL = maxTTL } else if req.Operation == logical.CreateOperation { roleEntry.MaxTTL = time.Duration(data.Get("max_ttl").(int)) * time.Second } roleTagStr, ok := data.GetOk("role_tag") if ok { roleEntry.RoleTag = roleTagStr.(string) // There is a limit of 127 characters on the tag key for AWS EC2 instances. // Complying to that requirement, do not allow the value of 'key' to be more than that. if len(roleEntry.RoleTag) > 127 { return logical.ErrorResponse("length of role tag exceeds the EC2 key limit of 127 characters"), nil } } else if req.Operation == logical.CreateOperation { roleEntry.RoleTag = data.Get("role_tag").(string) } if roleEntry.HMACKey == "" { roleEntry.HMACKey, err = uuid.GenerateUUID() if err != nil { return nil, fmt.Errorf("failed to generate role HMAC key: %v", err) } } entry, err := logical.StorageEntryJSON("role/"+roleName, roleEntry) if err != nil { return nil, err } if err := req.Storage.Put(entry); err != nil { return nil, err } if len(resp.Warnings()) == 0 { return nil, nil } return &resp, nil }
// pathLoginUpdate is used to create a Vault token by the EC2 instances // by providing the pkcs7 signature of the instance identity document // and a client created nonce. Client nonce is optional if 'disallow_reauthentication' // option is enabled on the registered role. func (b *backend) pathLoginUpdate( req *logical.Request, data *framework.FieldData) (*logical.Response, error) { identityDocB64 := data.Get("identity").(string) var identityDocBytes []byte var err error if identityDocB64 != "" { identityDocBytes, err = base64.StdEncoding.DecodeString(identityDocB64) if err != nil || len(identityDocBytes) == 0 { return logical.ErrorResponse("failed to base64 decode the instance identity document"), nil } } signatureB64 := data.Get("signature").(string) var signatureBytes []byte if signatureB64 != "" { signatureBytes, err = base64.StdEncoding.DecodeString(signatureB64) if err != nil { return logical.ErrorResponse("failed to base64 decode the SHA256 RSA signature of the instance identity document"), nil } } pkcs7B64 := data.Get("pkcs7").(string) // Either the pkcs7 signature of the instance identity document, or // the identity document itself along with its SHA256 RSA signature // needs to be provided. if pkcs7B64 == "" && (len(identityDocBytes) == 0 && len(signatureBytes) == 0) { return logical.ErrorResponse("either pkcs7 or a tuple containing the instance identity document and its SHA256 RSA signature needs to be provided"), nil } else if pkcs7B64 != "" && (len(identityDocBytes) != 0 && len(signatureBytes) != 0) { return logical.ErrorResponse("both pkcs7 and a tuple containing the instance identity document and its SHA256 RSA signature is supplied; provide only one"), nil } // Verify the signature of the identity document and unmarshal it var identityDocParsed *identityDocument if pkcs7B64 != "" { identityDocParsed, err = b.parseIdentityDocument(req.Storage, pkcs7B64) if err != nil { return nil, err } if identityDocParsed == nil { return logical.ErrorResponse("failed to verify the instance identity document using pkcs7"), nil } } else { identityDocParsed, err = b.verifyInstanceIdentitySignature(req.Storage, identityDocBytes, signatureBytes) if err != nil { return nil, err } if identityDocParsed == nil { return logical.ErrorResponse("failed to verify the instance identity document using the SHA256 RSA digest"), nil } } roleName := data.Get("role").(string) // If roleName is not supplied, a role in the name of the instance's AMI ID will be looked for if roleName == "" { roleName = identityDocParsed.AmiID } // Validate the instance ID by making a call to AWS EC2 DescribeInstances API // and fetching the instance description. Validation succeeds only if the // instance is in 'running' state. instanceDesc, err := b.validateInstance(req.Storage, identityDocParsed.InstanceID, identityDocParsed.Region) if err != nil { return logical.ErrorResponse(fmt.Sprintf("failed to verify instance ID: %v", err)), nil } // Get the entry for the role used by the instance roleEntry, err := b.lockedAWSRole(req.Storage, roleName) if err != nil { return nil, err } if roleEntry == nil { return logical.ErrorResponse(fmt.Sprintf("entry for role %q not found", roleName)), nil } // Verify that the AMI ID of the instance trying to login matches the // AMI ID specified as a constraint on the role if roleEntry.BoundAmiID != "" && identityDocParsed.AmiID != roleEntry.BoundAmiID { return logical.ErrorResponse(fmt.Sprintf("AMI ID %q does not belong to role %q", identityDocParsed.AmiID, roleName)), nil } // Verify that the AccountID of the instance trying to login matches the // AccountID specified as a constraint on the role if roleEntry.BoundAccountID != "" && identityDocParsed.AccountID != roleEntry.BoundAccountID { return logical.ErrorResponse(fmt.Sprintf("Account ID %q does not belong to role %q", identityDocParsed.AccountID, roleName)), nil } // Check if the IAM instance profile ARN of the instance trying to // login, matches the IAM instance profile ARN specified as a constraint // on the role. if roleEntry.BoundIamInstanceProfileARN != "" { if instanceDesc.Reservations[0].Instances[0].IamInstanceProfile == nil { return nil, fmt.Errorf("IAM instance profile in the instance description is nil") } if instanceDesc.Reservations[0].Instances[0].IamInstanceProfile.Arn == nil { return nil, fmt.Errorf("IAM instance profile ARN in the instance description is nil") } iamInstanceProfileARN := *instanceDesc.Reservations[0].Instances[0].IamInstanceProfile.Arn if !strings.HasPrefix(iamInstanceProfileARN, roleEntry.BoundIamInstanceProfileARN) { return logical.ErrorResponse(fmt.Sprintf("IAM instance profile ARN %q does not satisfy the constraint role %q", iamInstanceProfileARN, roleName)), nil } } // Check if the IAM role ARN of the instance trying to login, matches // the IAM role ARN specified as a constraint on the role. if roleEntry.BoundIamRoleARN != "" { if instanceDesc.Reservations[0].Instances[0].IamInstanceProfile == nil { return nil, fmt.Errorf("IAM instance profile in the instance description is nil") } if instanceDesc.Reservations[0].Instances[0].IamInstanceProfile.Arn == nil { return nil, fmt.Errorf("IAM instance profile ARN in the instance description is nil") } // Fetch the instance profile ARN from the instance description iamInstanceProfileARN := *instanceDesc.Reservations[0].Instances[0].IamInstanceProfile.Arn if iamInstanceProfileARN == "" { return nil, fmt.Errorf("IAM instance profile ARN in the instance description is empty") } // Extract out the instance profile name from the instance // profile ARN iamInstanceProfileARNSlice := strings.SplitAfter(iamInstanceProfileARN, ":instance-profile/") iamInstanceProfileName := iamInstanceProfileARNSlice[len(iamInstanceProfileARNSlice)-1] if iamInstanceProfileName == "" { return nil, fmt.Errorf("failed to extract out IAM instance profile name from IAM instance profile ARN") } // Use instance profile ARN to fetch the associated role ARN iamRoleARN, err := b.instanceIamRoleARN(req.Storage, iamInstanceProfileName, identityDocParsed.Region) if err != nil { return nil, fmt.Errorf("IAM role ARN could not be fetched: %v", err) } if iamRoleARN == "" { return nil, fmt.Errorf("IAM role ARN could not be fetched") } if !strings.HasPrefix(iamRoleARN, roleEntry.BoundIamRoleARN) { return logical.ErrorResponse(fmt.Sprintf("IAM role ARN %q does not satisfy the constraint role %q", iamRoleARN, roleName)), nil } } // Get the entry from the identity whitelist, if there is one storedIdentity, err := whitelistIdentityEntry(req.Storage, identityDocParsed.InstanceID) if err != nil { return nil, err } // disallowReauthentication value that gets cached at the stored // identity-whitelist entry is determined not just by the role entry. // If client explicitly sets nonce to be empty, it implies intent to // disable reauthentication. Also, role tag can override the 'false' // value with 'true' (the other way around is not allowed). // Read the value from the role entry disallowReauthentication := roleEntry.DisallowReauthentication clientNonce := "" // Check if the nonce is supplied by the client clientNonceRaw, clientNonceSupplied := data.GetOk("nonce") if clientNonceSupplied { clientNonce = clientNonceRaw.(string) // Nonce explicitly set to empty implies intent to disable // reauthentication by the client. Set a predefined nonce which // indicates reauthentication being disabled. if clientNonce == "" { clientNonce = reauthenticationDisabledNonce // Ensure that the intent lands in the whitelist disallowReauthentication = true } } // This is NOT a first login attempt from the client if storedIdentity != nil { // Check if the client nonce match the cached nonce and if the pending time // of the identity document is not before the pending time of the document // with which previous login was made. If 'allow_instance_migration' is // enabled on the registered role, client nonce requirement is relaxed. if err = validateMetadata(clientNonce, identityDocParsed.PendingTime, storedIdentity, roleEntry); err != nil { return logical.ErrorResponse(err.Error()), nil } // Don't let subsequent login attempts to bypass in initial // intent of disabling reauthentication, despite the properties // of role getting updated. For example: Role has the value set // to 'false', a role-tag login sets the value to 'true', then // role gets updated to not use a role-tag, and a login attempt // is made with role's value set to 'false'. Removing the entry // from the identity-whitelist should be the only way to be // able to login from the instance again. disallowReauthentication = disallowReauthentication || storedIdentity.DisallowReauthentication } // If we reach this point without erroring and if the client nonce was // not supplied, a first time login is implied and that the client // intends that the nonce be generated by the backend. Create a random // nonce to be associated for the instance ID. if !clientNonceSupplied { if clientNonce, err = uuid.GenerateUUID(); err != nil { return nil, fmt.Errorf("failed to generate random nonce") } } // Load the current values for max TTL and policies from the role entry, // before checking for overriding max TTL in the role tag. The shortest // max TTL is used to cap the token TTL; the longest max TTL is used to // make the whitelist entry as long as possible as it controls for replay // attacks. shortestMaxTTL := b.System().MaxLeaseTTL() longestMaxTTL := b.System().MaxLeaseTTL() if roleEntry.MaxTTL > time.Duration(0) && roleEntry.MaxTTL < shortestMaxTTL { shortestMaxTTL = roleEntry.MaxTTL } if roleEntry.MaxTTL > longestMaxTTL { longestMaxTTL = roleEntry.MaxTTL } policies := roleEntry.Policies rTagMaxTTL := time.Duration(0) if roleEntry.RoleTag != "" { // // Role tag is enabled on the role. // // Overwrite the policies with the ones returned from processing the role tag resp, err := b.handleRoleTagLogin(req.Storage, identityDocParsed, roleName, roleEntry, instanceDesc) if err != nil { return nil, err } if resp == nil { return logical.ErrorResponse("failed to fetch and verify the role tag"), nil } // If there are no policies on the role tag, policies on the role are inherited. // If policies on role tag are set, by this point, it is verified that it is a subset of the // policies on the role. So, apply only those. if len(resp.Policies) != 0 { policies = resp.Policies } // If roleEntry had disallowReauthentication set to 'true', do not reset it // to 'false' based on role tag having it not set. But, if role tag had it set, // be sure to override the value. if !disallowReauthentication { disallowReauthentication = resp.DisallowReauthentication } // Cache the value of role tag's max_ttl value rTagMaxTTL = resp.MaxTTL // Scope the shortestMaxTTL to the value set on the role tag if resp.MaxTTL > time.Duration(0) && resp.MaxTTL < shortestMaxTTL { shortestMaxTTL = resp.MaxTTL } if resp.MaxTTL > longestMaxTTL { longestMaxTTL = resp.MaxTTL } } // Save the login attempt in the identity whitelist currentTime := time.Now() if storedIdentity == nil { // Role, ClientNonce and CreationTime of the identity entry, // once set, should never change. storedIdentity = &whitelistIdentity{ Role: roleName, ClientNonce: clientNonce, CreationTime: currentTime, } } // DisallowReauthentication, PendingTime, LastUpdatedTime and // ExpirationTime may change. storedIdentity.LastUpdatedTime = currentTime storedIdentity.ExpirationTime = currentTime.Add(longestMaxTTL) storedIdentity.PendingTime = identityDocParsed.PendingTime storedIdentity.DisallowReauthentication = disallowReauthentication // Don't cache the nonce if DisallowReauthentication is set if storedIdentity.DisallowReauthentication { storedIdentity.ClientNonce = "" } // Sanitize the nonce to a reasonable length if len(clientNonce) > 128 && !storedIdentity.DisallowReauthentication { return logical.ErrorResponse("client nonce exceeding the limit of 128 characters"), nil } if err = setWhitelistIdentityEntry(req.Storage, identityDocParsed.InstanceID, storedIdentity); err != nil { return nil, err } resp := &logical.Response{ Auth: &logical.Auth{ Policies: policies, Metadata: map[string]string{ "instance_id": identityDocParsed.InstanceID, "region": identityDocParsed.Region, "role_tag_max_ttl": rTagMaxTTL.String(), "role": roleName, "ami_id": identityDocParsed.AmiID, }, LeaseOptions: logical.LeaseOptions{ Renewable: true, TTL: roleEntry.TTL, }, }, } // Return the nonce only if reauthentication is allowed if !disallowReauthentication { // Echo the client nonce back. If nonce param was not supplied // to the endpoint at all (setting it to empty string does not // qualify here), callers should extract out the nonce from // this field for reauthentication requests. resp.Auth.Metadata["nonce"] = clientNonce } // Cap the TTL value. shortestTTL := b.System().DefaultLeaseTTL() if roleEntry.TTL > time.Duration(0) && roleEntry.TTL < shortestTTL { shortestTTL = roleEntry.TTL } if shortestMaxTTL < shortestTTL { resp.AddWarning(fmt.Sprintf("Effective ttl of %q exceeded the effective max_ttl of %q; ttl value is capped appropriately", (shortestTTL / time.Second).String(), (shortestMaxTTL / time.Second).String())) shortestTTL = shortestMaxTTL } resp.Auth.TTL = shortestTTL return resp, nil }
// pathRoleTagUpdate is used to create an EC2 instance tag which will // identify the Vault resources that the instance will be authorized for. func (b *backend) pathRoleTagUpdate( req *logical.Request, data *framework.FieldData) (*logical.Response, error) { roleName := strings.ToLower(data.Get("role").(string)) if roleName == "" { return logical.ErrorResponse("missing role"), nil } // Fetch the role entry roleEntry, err := b.lockedAWSRole(req.Storage, roleName) if err != nil { return nil, err } if roleEntry == nil { return logical.ErrorResponse(fmt.Sprintf("entry not found for role %s", roleName)), nil } // If RoleTag is empty, disallow creation of tag. if roleEntry.RoleTag == "" { return logical.ErrorResponse("tag creation is not enabled for this role"), nil } // There should be a HMAC key present in the role entry if roleEntry.HMACKey == "" { // Not being able to find the HMACKey is an internal error return nil, fmt.Errorf("failed to find the HMAC key") } resp := &logical.Response{} // Instance ID is an optional field. instanceID := strings.ToLower(data.Get("instance_id").(string)) // If no policies field was not supplied, then the tag should inherit all the policies // on the role. But, it was provided, but set to empty explicitly, only "default" policy // should be inherited. So, by leaving the policies var unset to anything when it is not // supplied, we ensure that it inherits all the policies on the role. var policies []string policiesStr, ok := data.GetOk("policies") if ok { policies = policyutil.ParsePolicies(policiesStr.(string)) } if !strutil.StrListSubset(roleEntry.Policies, policies) { resp.AddWarning("Policies on the tag are not a subset of the policies set on the role. Login will not be allowed with this tag unless the role policies are updated.") } // This is an optional field. disallowReauthentication := data.Get("disallow_reauthentication").(bool) // This is an optional field. allowInstanceMigration := data.Get("allow_instance_migration").(bool) if allowInstanceMigration && !roleEntry.AllowInstanceMigration { resp.AddWarning("Role does not allow instance migration. Login will not be allowed with this tag unless the role value is updated.") } // max_ttl for the role tag should be less than the max_ttl set on the role. maxTTL := time.Duration(data.Get("max_ttl").(int)) * time.Second // max_ttl on the tag should not be greater than the system view's max_ttl value. if maxTTL > b.System().MaxLeaseTTL() { resp.AddWarning(fmt.Sprintf("Given max TTL of %d is greater than the mount maximum of %d seconds, and will be capped at login time.", maxTTL/time.Second, b.System().MaxLeaseTTL()/time.Second)) } // If max_ttl is set for the role, check the bounds for tag's max_ttl value using that. if roleEntry.MaxTTL != time.Duration(0) && maxTTL > roleEntry.MaxTTL { resp.AddWarning(fmt.Sprintf("Given max TTL of %d is greater than the role maximum of %d seconds, and will be capped at login time.", maxTTL/time.Second, roleEntry.MaxTTL/time.Second)) } if maxTTL < time.Duration(0) { return logical.ErrorResponse("max_ttl cannot be negative"), nil } // Create a random nonce. nonce, err := createRoleTagNonce() if err != nil { return nil, err } // Create a role tag out of all the information provided. rTagValue, err := createRoleTagValue(&roleTag{ Version: roleTagVersion, Role: roleName, Nonce: nonce, Policies: policies, MaxTTL: maxTTL, InstanceID: instanceID, DisallowReauthentication: disallowReauthentication, AllowInstanceMigration: allowInstanceMigration, }, roleEntry) if err != nil { return nil, err } // Return the key to be used for the tag and the value to be used for that tag key. // This key value pair should be set on the EC2 instance. resp.Data = map[string]interface{}{ "tag_key": roleEntry.RoleTag, "tag_value": rTagValue, } return resp, nil }
func (b *backend) pathCAGenerateRoot( req *logical.Request, data *framework.FieldData) (*logical.Response, error) { var err error exported, format, role, errorResp := b.getGenerationParams(data) if errorResp != nil { return errorResp, nil } maxPathLengthIface, ok := data.GetOk("max_path_length") if ok { maxPathLength := maxPathLengthIface.(int) role.MaxPathLength = &maxPathLength } parsedBundle, err := generateCert(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 } } 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{}{ "expiration": int64(parsedBundle.Certificate.NotAfter.Unix()), "serial_number": cb.SerialNumber, }, } switch format { case "pem": resp.Data["certificate"] = cb.Certificate resp.Data["issuing_ca"] = cb.IssuingCA if exported { resp.Data["private_key"] = cb.PrivateKey resp.Data["private_key_type"] = cb.PrivateKeyType } case "pem_bundle": resp.Data["issuing_ca"] = cb.IssuingCA if exported { resp.Data["private_key"] = cb.PrivateKey resp.Data["private_key_type"] = cb.PrivateKeyType resp.Data["certificate"] = fmt.Sprintf("%s\n%s\n%s", cb.PrivateKey, cb.Certificate, cb.IssuingCA) } else { resp.Data["certificate"] = fmt.Sprintf("%s\n%s", cb.Certificate, cb.IssuingCA) } case "der": resp.Data["certificate"] = base64.StdEncoding.EncodeToString(parsedBundle.CertificateBytes) resp.Data["issuing_ca"] = base64.StdEncoding.EncodeToString(parsedBundle.IssuingCABytes) if exported { resp.Data["private_key"] = base64.StdEncoding.EncodeToString(parsedBundle.PrivateKeyBytes) resp.Data["private_key_type"] = cb.PrivateKeyType } } // Store it as the CA bundle 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 } // Also store it as just the certificate identified by serial number, so it // can be revoked err = req.Storage.Put(&logical.StorageEntry{ Key: "certs/" + cb.SerialNumber, Value: parsedBundle.CertificateBytes, }) if err != nil { return nil, fmt.Errorf("Unable to store certificate locally") } // For ease of later use, also store just the certificate at a known // location entry.Key = "ca" entry.Value = parsedBundle.CertificateBytes err = req.Storage.Put(entry) if err != nil { return nil, err } // Build a fresh CRL err = buildCRL(b, req) if err != nil { return nil, err } if parsedBundle.Certificate.MaxPathLen == 0 { resp.AddWarning("Max path length of the generated certificate is zero. This certificate cannot be used to issue intermediate CA certificates.") } return resp, nil }
func pathConfigWrite( req *logical.Request, d *framework.FieldData) (*logical.Response, error) { name := d.Get("name").(string) // Check if the policy already exists policy, err := getPolicy(req, name) if err != nil { return nil, err } if policy == nil { return logical.ErrorResponse( fmt.Sprintf("no existing role named %s could be found", name)), logical.ErrInvalidRequest } resp := &logical.Response{} persistNeeded := false minDecryptionVersionRaw, ok := d.GetOk("min_decryption_version") if ok { minDecryptionVersion := minDecryptionVersionRaw.(int) if minDecryptionVersion < 0 { return logical.ErrorResponse("min decryption version cannot be negative"), nil } if minDecryptionVersion == 0 { minDecryptionVersion = 1 resp.AddWarning("since Vault 0.3, transit key numbering starts at 1; forcing minimum to 1") } if minDecryptionVersion > 0 && minDecryptionVersion != policy.MinDecryptionVersion { if minDecryptionVersion > policy.LatestVersion { return logical.ErrorResponse( fmt.Sprintf("cannot set min decryption version of %d, latest key version is %d", minDecryptionVersion, policy.LatestVersion)), nil } policy.MinDecryptionVersion = minDecryptionVersion persistNeeded = true } } allowDeletionInt, ok := d.GetOk("deletion_allowed") if ok { allowDeletion := allowDeletionInt.(bool) if allowDeletion != policy.DeletionAllowed { policy.DeletionAllowed = allowDeletion persistNeeded = true } } // Add this as a guard here before persisting since we now require the min // decryption version to start at 1; even if it's not explicitly set here, // force the upgrade if policy.MinDecryptionVersion == 0 { policy.MinDecryptionVersion = 1 persistNeeded = true } if !persistNeeded { return nil, nil } return resp, policy.Persist(req.Storage) }
func (ts *TokenStore) tokenStoreRoleCreateUpdate( req *logical.Request, data *framework.FieldData) (*logical.Response, error) { name := data.Get("role_name").(string) if name == "" { return logical.ErrorResponse("role name cannot be empty"), nil } entry, err := ts.tokenStoreRole(name) if err != nil { return nil, err } // Due to the existence check, entry will only be nil if it's a create // operation, so just create a new one if entry == nil { entry = &tsRoleEntry{ Name: name, } } // In this series of blocks, if we do not find a user-provided value and // it's a creation operation, we call data.Get to get the appropriate // default orphanInt, ok := data.GetOk("orphan") if ok { entry.Orphan = orphanInt.(bool) } else if req.Operation == logical.CreateOperation { entry.Orphan = data.Get("orphan").(bool) } periodInt, ok := data.GetOk("period") if ok { entry.Period = time.Second * time.Duration(periodInt.(int)) } else if req.Operation == logical.CreateOperation { entry.Period = time.Second * time.Duration(data.Get("period").(int)) } var resp *logical.Response explicitMaxTTLInt, ok := data.GetOk("explicit_max_ttl") if ok { entry.ExplicitMaxTTL = time.Second * time.Duration(explicitMaxTTLInt.(int)) } else if req.Operation == logical.CreateOperation { entry.ExplicitMaxTTL = time.Second * time.Duration(data.Get("explicit_max_ttl").(int)) } if entry.ExplicitMaxTTL != 0 { sysView := ts.System() if sysView.MaxLeaseTTL() != time.Duration(0) && entry.ExplicitMaxTTL > sysView.MaxLeaseTTL() { if resp == nil { resp = &logical.Response{} } resp.AddWarning(fmt.Sprintf( "Given explicit max TTL of %d is greater than system/mount allowed value of %d seconds; until this is fixed attempting to create tokens against this role will result in an error", entry.ExplicitMaxTTL.Seconds(), sysView.MaxLeaseTTL().Seconds())) } } pathSuffixInt, ok := data.GetOk("path_suffix") if ok { pathSuffix := pathSuffixInt.(string) if pathSuffix != "" { matched := pathSuffixSanitize.MatchString(pathSuffix) if !matched { return logical.ErrorResponse(fmt.Sprintf( "given role path suffix contains invalid characters; must match %s", pathSuffixSanitize.String())), nil } entry.PathSuffix = pathSuffix } } else if req.Operation == logical.CreateOperation { entry.PathSuffix = data.Get("path_suffix").(string) } allowedPoliciesInt, ok := data.GetOk("allowed_policies") if ok { allowedPolicies := allowedPoliciesInt.(string) if allowedPolicies != "" { entry.AllowedPolicies = strings.Split(allowedPolicies, ",") } } else if req.Operation == logical.CreateOperation { entry.AllowedPolicies = strings.Split(data.Get("allowed_policies").(string), ",") } // Explicit max TTLs and periods cannot be used at the same time since the // purpose of a periodic token is to escape max TTL semantics if entry.Period > 0 && entry.ExplicitMaxTTL > 0 { return logical.ErrorResponse("a role cannot be used to issue both periodic tokens and tokens with explicit max TTLs"), logical.ErrInvalidRequest } // Store it jsonEntry, err := logical.StorageEntryJSON(fmt.Sprintf("%s%s", rolesPrefix, name), entry) if err != nil { return nil, err } if err := ts.view.Put(jsonEntry); err != nil { return nil, err } return resp, nil }