func (b *backend) populateCRLs(storage logical.Storage) error { b.crlUpdateMutex.Lock() defer b.crlUpdateMutex.Unlock() keys, err := storage.List("crls/") if err != nil { return fmt.Errorf("error listing CRLs: %v", err) } if keys == nil || len(keys) == 0 { return nil } for _, key := range keys { entry, err := storage.Get("crls/" + key) if err != nil { return fmt.Errorf("error loading CRL %s: %v", key, err) } if entry == nil { continue } var crlInfo CRLInfo err = entry.DecodeJSON(&crlInfo) if err != nil { return fmt.Errorf("error decoding CRL %s: %v", key, err) } b.crls[key] = crlInfo } return nil }
// DB returns the database connection. func (b *backend) DB(s logical.Storage) (*sql.DB, error) { b.logger.Trace("postgres/db: enter") defer b.logger.Trace("postgres/db: exit") b.lock.Lock() defer b.lock.Unlock() // If we already have a DB, we got it! if b.db != nil { if err := b.db.Ping(); err == nil { return b.db, nil } // If the ping was unsuccessful, close it and ignore errors as we'll be // reestablishing anyways b.db.Close() } // Otherwise, attempt to make connection entry, err := s.Get("config/connection") if err != nil { return nil, err } if entry == nil { return nil, fmt.Errorf("configure the DB connection with config/connection first") } var connConfig connectionConfig if err := entry.DecodeJSON(&connConfig); err != nil { return nil, err } conn := connConfig.ConnectionURL if len(conn) == 0 { conn = connConfig.ConnectionString } // Ensure timezone is set to UTC for all the conenctions if strings.HasPrefix(conn, "postgres://") || strings.HasPrefix(conn, "postgresql://") { if strings.Contains(conn, "?") { conn += "&timezone=utc" } else { conn += "?timezone=utc" } } else { conn += " timezone=utc" } b.db, err = sql.Open("postgres", conn) if err != nil { return nil, err } // Set some connection pool settings. We don't need much of this, // since the request rate shouldn't be high. b.db.SetMaxOpenConns(connConfig.MaxOpenConnections) b.db.SetMaxIdleConns(connConfig.MaxIdleConnections) return b.db, nil }
// createSecretIDAccessorEntry creates an identifier for the SecretID. A storage index, // mapping the accessor to the SecretID is also created. This method should // be called when the lock for the corresponding SecretID is held. func (b *backend) createSecretIDAccessorEntry(s logical.Storage, entry *secretIDStorageEntry, secretIDHMAC string) error { // Create a random accessor accessorUUID, err := uuid.GenerateUUID() if err != nil { return err } entry.SecretIDAccessor = accessorUUID // Create index entry, mapping the accessor to the token ID entryIndex := "accessor/" + b.salt.SaltID(entry.SecretIDAccessor) accessorLock := b.secretIDAccessorLock(accessorUUID) accessorLock.Lock() defer accessorLock.Unlock() if entry, err := logical.StorageEntryJSON(entryIndex, &secretIDAccessorStorageEntry{ SecretIDHMAC: secretIDHMAC, }); err != nil { return err } else if err = s.Put(entry); err != nil { return fmt.Errorf("failed to persist accessor index entry: %s", err) } return nil }
// Takes an IP address and role name and checks if the IP is part // of CIDR blocks belonging to the role. func roleContainsIP(s logical.Storage, roleName string, ip string) (bool, error) { if roleName == "" { return false, fmt.Errorf("missing role name") } if ip == "" { return false, fmt.Errorf("missing ip") } roleEntry, err := s.Get(fmt.Sprintf("roles/%s", roleName)) if err != nil { return false, fmt.Errorf("error retrieving role '%s'", err) } if roleEntry == nil { return false, fmt.Errorf("role '%s' not found", roleName) } var role sshRole if err := roleEntry.DecodeJSON(&role); err != nil { return false, fmt.Errorf("error decoding role '%s'", roleName) } if matched, err := cidrListContainsIP(ip, role.CIDRList); err != nil { return false, err } else { return matched, nil } }
// DB returns the database connection. func (b *backend) Client(s logical.Storage) (*rabbithole.Client, error) { b.lock.Lock() defer b.lock.Unlock() // If we already have a client, we got it! if b.client != nil { return b.client, nil } // Otherwise, attempt to make connection entry, err := s.Get("config/connection") if err != nil { return nil, err } if entry == nil { return nil, fmt.Errorf("configure the client connection with config/connection first") } var connConfig connectionConfig if err := entry.DecodeJSON(&connConfig); err != nil { return nil, err } b.client, err = rabbithole.NewClient(connConfig.URI, connConfig.Username, connConfig.Password) if err != nil { return nil, err } return b.client, nil }
// loadTrustedCerts is used to load all the trusted certificates from the backend func (b *backend) loadTrustedCerts(store logical.Storage) (pool *x509.CertPool, trusted []*ParsedCert) { pool = x509.NewCertPool() names, err := store.List("cert/") if err != nil { b.Logger().Printf("[ERR] cert: failed to list trusted certs: %v", err) return } for _, name := range names { entry, err := b.Cert(store, strings.TrimPrefix(name, "cert/")) if err != nil { b.Logger().Printf("[ERR] cert: failed to load trusted certs '%s': %v", name, err) continue } parsed := parsePEM([]byte(entry.Certificate)) if len(parsed) == 0 { b.Logger().Printf("[ERR] cert: failed to parse certificate for '%s'", name) continue } for _, p := range parsed { pool.AddCert(p) } // Create a ParsedCert entry trusted = append(trusted, &ParsedCert{ Entry: entry, Certificates: parsed, }) } return }
// nonLockedAWSPublicCertificateEntry reads the certificate information from // the storage. This method does not acquire lock before reading the storage. // If locking is desired, use lockedAWSPublicCertificateEntry instead. func (b *backend) nonLockedAWSPublicCertificateEntry(s logical.Storage, certName string) (*awsPublicCert, error) { entry, err := s.Get("config/certificate/" + certName) if err != nil { return nil, err } if entry == nil { return nil, nil } var certEntry awsPublicCert if err := entry.DecodeJSON(&certEntry); err != nil { return nil, err } // Handle upgrade for certificate type persistNeeded := false if certEntry.Type == "" { certEntry.Type = "pkcs7" persistNeeded = true } if persistNeeded { if err := b.nonLockedSetAWSPublicCertificateEntry(s, certName, &certEntry); err != nil { return nil, err } } return &certEntry, nil }
// DB returns the database connection. func (b *backend) DB(s logical.Storage) (*sql.DB, error) { b.lock.Lock() defer b.lock.Unlock() // If we already have a DB, we got it! if b.db != nil { return b.db, nil } // Otherwise, attempt to make connection entry, err := s.Get("config/connection") if err != nil { return nil, err } if entry == nil { return nil, fmt.Errorf("configure the DB connection with config/connection first") } var conn string if err := entry.DecodeJSON(&conn); err != nil { return nil, err } b.db, err = sql.Open("postgres", conn) if err != nil { return nil, err } // Set some connection pool settings. We don't need much of this, // since the request rate shouldn't be high. b.db.SetMaxOpenConns(2) return b.db, nil }
// flushRoleSecrets deletes all the SecretIDs that belong to the given // RoleID. func (b *backend) flushRoleSecrets(s logical.Storage, roleName, hmacKey string) error { roleNameHMAC, err := createHMAC(hmacKey, roleName) if err != nil { return fmt.Errorf("failed to create HMAC of role_name: %s", err) } // Acquire the custom lock to perform listing of SecretIDs customLock := b.secretIDLock("") customLock.RLock() defer customLock.RUnlock() secretIDHMACs, err := s.List(fmt.Sprintf("secret_id/%s/", roleNameHMAC)) if err != nil { return err } for _, secretIDHMAC := range secretIDHMACs { // Acquire the lock belonging to the SecretID lock := b.secretIDLock(secretIDHMAC) lock.Lock() entryIndex := fmt.Sprintf("secret_id/%s/%s", roleNameHMAC, secretIDHMAC) if err := s.Delete(entryIndex); err != nil { lock.Unlock() return fmt.Errorf("error deleting SecretID %s from storage: %s", secretIDHMAC, err) } lock.Unlock() } return nil }
func clientIAM(s logical.Storage) (*iam.IAM, error) { entry, err := s.Get("config/root") if err != nil { return nil, err } if entry == nil { return nil, fmt.Errorf( "root credentials haven't been configured. Please configure\n" + "them at the 'config/root' endpoint") } var config rootConfig if err := entry.DecodeJSON(&config); err != nil { return nil, fmt.Errorf("error reading root configuration: %s", err) } creds := credentials.NewStaticCredentials(config.AccessKey, config.SecretKey, "") awsConfig := &aws.Config{ Credentials: creds, Region: aws.String(config.Region), HTTPClient: cleanhttp.DefaultClient(), } return iam.New(session.New(awsConfig)), nil }
// DB returns the database connection. func (b *backend) DB(s logical.Storage) (*gocql.Session, error) { b.lock.Lock() defer b.lock.Unlock() // If we already have a DB, we got it! if b.session != nil { return b.session, nil } entry, err := s.Get("config/connection") if err != nil { return nil, err } if entry == nil { return nil, fmt.Errorf("Configure the DB connection with config/connection first") } config := &sessionConfig{} if err := entry.DecodeJSON(config); err != nil { return nil, err } return createSession(config, s) }
// nonLockedAWSRole returns the properties set on the given role. This method // does not acquire the read lock before reading the role from the storage. If // locking is desired, use lockedAWSRole instead. func (b *backend) nonLockedAWSRole(s logical.Storage, roleName string) (*awsRoleEntry, error) { if roleName == "" { return nil, fmt.Errorf("missing role name") } entry, err := s.Get("role/" + strings.ToLower(roleName)) if err != nil { return nil, err } if entry == nil { return nil, nil } var result awsRoleEntry if err := entry.DecodeJSON(&result); err != nil { return nil, err } // Check if the value held by role ARN field is actually an instance profile ARN if result.BoundIamRoleARN != "" && strings.Contains(result.BoundIamRoleARN, ":instance-profile/") { // If yes, move it to the correct field result.BoundIamInstanceProfileARN = result.BoundIamRoleARN // Reset the old field result.BoundIamRoleARN = "" // Save the update if err = b.nonLockedSetAWSRole(s, roleName, &result); err != nil { return nil, fmt.Errorf("failed to move instance profile ARN to bound_iam_instance_profile_arn field") } } return &result, nil }
func getRootConfig(s logical.Storage) (*aws.Config, error) { credsConfig := &awsutil.CredentialsConfig{} entry, err := s.Get("config/root") if err != nil { return nil, err } if entry != nil { var config rootConfig if err := entry.DecodeJSON(&config); err != nil { return nil, fmt.Errorf("error reading root configuration: %s", err) } credsConfig.AccessKey = config.AccessKey credsConfig.SecretKey = config.SecretKey credsConfig.Region = config.Region } if credsConfig.Region == "" { credsConfig.Region = "us-east-1" } credsConfig.HTTPClient = cleanhttp.DefaultClient() creds, err := credsConfig.GenerateCredentialChain() if err != nil { return nil, err } return &aws.Config{ Credentials: creds, Region: aws.String(credsConfig.Region), HTTPClient: cleanhttp.DefaultClient(), }, nil }
func (b *backend) setUser(s logical.Storage, username string, userEntry *UserEntry) error { entry, err := logical.StorageEntryJSON("user/"+username, userEntry) if err != nil { return err } return s.Put(entry) }
// NewSalt creates a new salt based on the configuration func NewSalt(view logical.Storage, config *Config) (*Salt, error) { // Setup the configuration if config == nil { config = &Config{} } if config.Location == "" { config.Location = DefaultLocation } if config.HashFunc == nil { config.HashFunc = SHA256Hash } // Create the salt s := &Salt{ config: config, } // Look for the salt raw, err := view.Get(config.Location) if err != nil { return nil, fmt.Errorf("failed to read salt: %v", err) } // Restore the salt if it exists if raw != nil { s.salt = string(raw.Value) } // Generate a new salt if necessary if s.salt == "" { s.salt, err = uuid.GenerateUUID() if err != nil { return nil, fmt.Errorf("failed to generate uuid: %v", err) } s.generated = true if view != nil { raw := &logical.StorageEntry{ Key: config.Location, Value: []byte(s.salt), } if err := view.Put(raw); err != nil { return nil, fmt.Errorf("failed to persist salt: %v", err) } } } if config.HMAC != nil { if len(config.HMACType) == 0 { return nil, fmt.Errorf("HMACType must be defined") } s.hmacType = config.HMACType } return s, nil }
// Stores an instance ID and the information required to validate further login/renewal attempts from // the same instance ID. func setWhitelistIdentityEntry(s logical.Storage, instanceID string, identity *whitelistIdentity) error { entry, err := logical.StorageEntryJSON("whitelist/identity/"+instanceID, identity) if err != nil { return err } if err := s.Put(entry); err != nil { return err } return nil }
// Put writes the structure. func (p *PathStruct) Put(s logical.Storage, v map[string]interface{}) error { bytes, err := json.Marshal(v) if err != nil { return err } return s.Put(&logical.StorageEntry{ Key: fmt.Sprintf("struct/%s", p.Name), Value: bytes, }) }
// ListWAL lists all the entries in the WAL. func ListWAL(s logical.Storage) ([]string, error) { keys, err := s.List(WALPrefix) if err != nil { return nil, err } for i, k := range keys { keys[i] = strings.TrimPrefix(k, WALPrefix) } return keys, nil }
// Stores the given list of roles at zeroaddress endpoint func (b *backend) putZeroAddressRoles(s logical.Storage, roles []string) error { entry, err := logical.StorageEntryJSON("config/zeroaddress", &zeroAddressRoles{ Roles: roles, }) if err != nil { return err } if err := s.Put(entry); err != nil { return err } return nil }
// DB returns the default database connection. func (b *backend) DB(s logical.Storage) (*sql.DB, error) { b.lock.Lock() defer b.lock.Unlock() // If we already have a DB, we got it! if b.db != nil { if err := b.db.Ping(); err == nil { return b.db, nil } // If the ping was unsuccessful, close it and ignore errors as we'll be // reestablishing anyways b.db.Close() } // Otherwise, attempt to make connection entry, err := s.Get("config/connection") if err != nil { return nil, err } if entry == nil { return nil, fmt.Errorf("configure the DB connection with config/connection first") } var connConfig connectionConfig if err := entry.DecodeJSON(&connConfig); err != nil { return nil, err } connString := connConfig.ConnectionString db, err := sql.Open("mssql", connString) if err != nil { return nil, err } // Set some connection pool settings. We don't need much of this, // since the request rate shouldn't be high. db.SetMaxOpenConns(connConfig.MaxOpenConnections) stmt, err := db.Prepare("SELECT db_name();") if err != nil { return nil, err } defer stmt.Close() err = stmt.QueryRow().Scan(&b.defaultDb) if err != nil { return nil, err } b.db = db return b.db, nil }
// List reads the keys under a given path func (p *PathMap) List(s logical.Storage, prefix string) ([]string, error) { stripPrefix := fmt.Sprintf("struct/map/%s/", p.Name) fullPrefix := fmt.Sprintf("%s%s", stripPrefix, prefix) out, err := s.List(fullPrefix) if err != nil { return nil, err } stripped := make([]string, len(out)) for idx, k := range out { stripped[idx] = strings.TrimPrefix(k, stripPrefix) } return stripped, nil }
// DB returns the database connection. func (b *backend) DB(s logical.Storage) (*sql.DB, error) { b.lock.Lock() defer b.lock.Unlock() // If we already have a DB, we got it! if b.db != nil { return b.db, nil } // Otherwise, attempt to make connection entry, err := s.Get("config/connection") if err != nil { return nil, err } if entry == nil { return nil, fmt.Errorf("configure the DB connection with config/connection first") } var connConfig connectionConfig if err := entry.DecodeJSON(&connConfig); err != nil { return nil, err } conn := connConfig.ConnectionString if len(conn) == 0 { conn = connConfig.ConnectionURL } // Ensure timezone is set to UTC for all the conenctions if strings.HasPrefix(conn, "postgres://") || strings.HasPrefix(conn, "postgresql://") { var err error conn, err = pq.ParseURL(conn) if err != nil { return nil, err } } conn += " timezone=utc" b.db, err = sql.Open("postgres", conn) if err != nil { return nil, err } // Set some connection pool settings. We don't need much of this, // since the request rate shouldn't be high. b.db.SetMaxOpenConns(connConfig.MaxOpenConnections) b.db.SetMaxIdleConns(connConfig.MaxIdleConnections) return b.db, nil }
// deleteSecretIDAccessorEntry deletes the storage index mapping the accessor to a SecretID. func (b *backend) deleteSecretIDAccessorEntry(s logical.Storage, secretIDAccessor string) error { accessorEntryIndex := "accessor/" + b.salt.SaltID(secretIDAccessor) accessorLock := b.secretIDAccessorLock(secretIDAccessor) accessorLock.Lock() defer accessorLock.Unlock() // Delete the accessor of the SecretID first if err := s.Delete(accessorEntryIndex); err != nil { return fmt.Errorf("failed to delete accessor storage entry: %s", err) } return nil }
func (b *backend) Cert(s logical.Storage, n string) (*CertEntry, error) { entry, err := s.Get("cert/" + strings.ToLower(n)) if err != nil { return nil, err } if entry == nil { return nil, nil } var result CertEntry if err := entry.DecodeJSON(&result); err != nil { return nil, err } return &result, nil }
func (b *backend) nonLockedBlacklistRoleTagEntry(s logical.Storage, tag string) (*roleTagBlacklistEntry, error) { entry, err := s.Get("blacklist/roletag/" + base64.StdEncoding.EncodeToString([]byte(tag))) if err != nil { return nil, err } if entry == nil { return nil, nil } var result roleTagBlacklistEntry if err := entry.DecodeJSON(&result); err != nil { return nil, err } return &result, nil }
// Fetch the client configuration required to access the AWS API. func (b *backend) nonLockedClientConfigEntry(s logical.Storage) (*clientConfig, error) { entry, err := s.Get("config/client") if err != nil { return nil, err } if entry == nil { return nil, nil } var result clientConfig if err := entry.DecodeJSON(&result); err != nil { return nil, err } return &result, nil }
func (b *backend) getKey(s logical.Storage, n string) (*sshHostKey, error) { entry, err := s.Get("keys/" + n) if err != nil { return nil, err } if entry == nil { return nil, nil } var result sshHostKey if err := entry.DecodeJSON(&result); err != nil { return nil, err } return &result, nil }
func (b *backend) nonLockedAWSRole(s logical.Storage, role string) (*awsRoleEntry, error) { entry, err := s.Get("role/" + strings.ToLower(role)) if err != nil { return nil, err } if entry == nil { return nil, nil } var result awsRoleEntry if err := entry.DecodeJSON(&result); err != nil { return nil, err } return &result, nil }
// Internal version of the above that does no locking func (b *backend) nonLockedAWSPublicCertificateEntry(s logical.Storage, certName string) (*awsPublicCert, error) { entry, err := s.Get("config/certificate/" + certName) if err != nil { return nil, err } if entry == nil { return nil, nil } var result awsPublicCert if err := entry.DecodeJSON(&result); err != nil { return nil, err } return &result, nil }
// Config returns the configuration for this backend. func (b *backend) Config(s logical.Storage) (*config, error) { entry, err := s.Get("config") if err != nil { return nil, err } var result config if entry != nil { if err := entry.DecodeJSON(&result); err != nil { return nil, fmt.Errorf("error reading configuration: %s", err) } } return &result, nil }