2022-09-06 07:50:15 -07:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
|
// SPDX-License-Identifier: BUSL-1.1
|
2023-02-02 17:15:17 -08:00
|
|
|
|
2021-06-22 22:26:26 -04:00
|
|
|
package vault
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
|
|
"github.com/hashicorp/boundary/internal/db/timestamp"
|
|
|
|
|
"github.com/hashicorp/boundary/internal/errors"
|
|
|
|
|
"github.com/hashicorp/boundary/internal/kms"
|
2022-03-16 17:04:07 -04:00
|
|
|
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
|
|
|
|
|
"github.com/hashicorp/go-kms-wrapping/v2/extras/structwrapping"
|
2021-06-22 22:26:26 -04:00
|
|
|
)
|
|
|
|
|
|
2022-09-06 07:50:15 -07:00
|
|
|
func (r *Repository) lookupClientStore(ctx context.Context, publicId string) (*clientStore, error) {
|
|
|
|
|
const op = "vault.(Repository).lookupClientStore"
|
2021-06-22 22:26:26 -04:00
|
|
|
if publicId == "" {
|
2021-07-29 18:52:56 -06:00
|
|
|
return nil, errors.New(ctx, errors.InvalidParameter, op, "no public id")
|
2021-06-22 22:26:26 -04:00
|
|
|
}
|
2022-09-06 07:50:15 -07:00
|
|
|
ps := allocClientStore()
|
2022-10-25 18:29:38 -05:00
|
|
|
if err := r.reader.LookupWhere(ctx, &ps, "public_id = ?", []any{publicId}); err != nil {
|
2021-06-22 22:26:26 -04:00
|
|
|
if errors.IsNotFoundError(err) {
|
|
|
|
|
return nil, nil
|
|
|
|
|
}
|
2021-07-29 18:52:56 -06:00
|
|
|
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for: %s", publicId)))
|
2021-06-22 22:26:26 -04:00
|
|
|
}
|
|
|
|
|
|
2022-07-20 16:24:46 -07:00
|
|
|
databaseWrapper, err := r.kms.GetWrapper(ctx, ps.ProjectId, kms.KeyPurposeDatabase)
|
2021-06-22 22:26:26 -04:00
|
|
|
if err != nil {
|
2021-07-29 18:52:56 -06:00
|
|
|
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get database wrapper"))
|
2021-06-22 22:26:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := ps.decrypt(ctx, databaseWrapper); err != nil {
|
2021-07-29 18:52:56 -06:00
|
|
|
return nil, errors.Wrap(ctx, err, op)
|
2021-06-22 22:26:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ps, nil
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-06 07:50:15 -07:00
|
|
|
// clientStore is a Vault credential store that contains all the data needed to create
|
|
|
|
|
// a Vault client. If the Vault token for the store is expired all token data will be null
|
|
|
|
|
// other than the status of expired.
|
|
|
|
|
type clientStore struct {
|
|
|
|
|
PublicId string `gorm:"primary_key"`
|
|
|
|
|
ProjectId string
|
|
|
|
|
DeleteTime *timestamp.Timestamp
|
|
|
|
|
VaultAddress string
|
|
|
|
|
Namespace string
|
|
|
|
|
CaCert []byte
|
|
|
|
|
TlsServerName string
|
|
|
|
|
TlsSkipVerify bool
|
|
|
|
|
WorkerFilter string
|
|
|
|
|
TokenHmac []byte
|
|
|
|
|
Token TokenSecret
|
|
|
|
|
CtToken []byte
|
|
|
|
|
TokenRenewalTime *timestamp.Timestamp
|
|
|
|
|
TokenKeyId string
|
|
|
|
|
TokenStatus string
|
|
|
|
|
ClientCert []byte
|
|
|
|
|
ClientKeyId string
|
|
|
|
|
ClientKey KeySecret
|
|
|
|
|
CtClientKey []byte
|
2021-06-22 22:26:26 -04:00
|
|
|
}
|
|
|
|
|
|
2022-09-06 07:50:15 -07:00
|
|
|
func allocClientStore() *clientStore {
|
|
|
|
|
return &clientStore{}
|
2021-06-22 22:26:26 -04:00
|
|
|
}
|
|
|
|
|
|
2022-09-06 07:50:15 -07:00
|
|
|
func (ps *clientStore) toCredentialStore() *CredentialStore {
|
2021-06-22 22:26:26 -04:00
|
|
|
cs := allocCredentialStore()
|
|
|
|
|
cs.PublicId = ps.PublicId
|
2022-07-20 16:24:46 -07:00
|
|
|
cs.ProjectId = ps.ProjectId
|
2021-06-22 22:26:26 -04:00
|
|
|
cs.DeleteTime = ps.DeleteTime
|
|
|
|
|
cs.VaultAddress = ps.VaultAddress
|
|
|
|
|
cs.Namespace = ps.Namespace
|
|
|
|
|
cs.CaCert = ps.CaCert
|
|
|
|
|
cs.TlsServerName = ps.TlsServerName
|
|
|
|
|
cs.TlsSkipVerify = ps.TlsSkipVerify
|
2022-07-22 09:13:49 -04:00
|
|
|
cs.WorkerFilter = ps.WorkerFilter
|
2021-06-22 22:26:26 -04:00
|
|
|
cs.privateToken = ps.token()
|
|
|
|
|
if ps.ClientCert != nil {
|
|
|
|
|
cert := allocClientCertificate()
|
2022-09-06 07:50:15 -07:00
|
|
|
cert.StoreId = ps.PublicId
|
2021-06-22 22:26:26 -04:00
|
|
|
cert.Certificate = ps.ClientCert
|
|
|
|
|
cert.CtCertificateKey = ps.CtClientKey
|
|
|
|
|
cert.KeyId = ps.ClientKeyId
|
|
|
|
|
cs.privateClientCert = cert
|
|
|
|
|
}
|
|
|
|
|
return cs
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-06 07:50:15 -07:00
|
|
|
func (ps *clientStore) token() *Token {
|
2021-06-22 22:26:26 -04:00
|
|
|
if ps.TokenHmac != nil {
|
|
|
|
|
tk := allocToken()
|
|
|
|
|
tk.TokenHmac = ps.TokenHmac
|
2022-09-06 07:50:15 -07:00
|
|
|
tk.Status = ps.TokenStatus
|
2021-06-22 22:26:26 -04:00
|
|
|
tk.CtToken = ps.CtToken
|
|
|
|
|
tk.KeyId = ps.TokenKeyId
|
|
|
|
|
|
|
|
|
|
return tk
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-06 07:50:15 -07:00
|
|
|
func (ps *clientStore) decrypt(ctx context.Context, cipher wrapping.Wrapper) error {
|
|
|
|
|
const op = "vault.(clientStore).decrypt"
|
2021-06-22 22:26:26 -04:00
|
|
|
|
|
|
|
|
if ps.CtToken != nil {
|
|
|
|
|
type ptk struct {
|
|
|
|
|
Token []byte `wrapping:"pt,token_data"`
|
|
|
|
|
CtToken []byte `wrapping:"ct,token_data"`
|
|
|
|
|
}
|
|
|
|
|
ptkv := &ptk{
|
|
|
|
|
CtToken: ps.CtToken,
|
|
|
|
|
}
|
|
|
|
|
if err := structwrapping.UnwrapStruct(ctx, cipher, ptkv, nil); err != nil {
|
2021-07-29 18:52:56 -06:00
|
|
|
return errors.Wrap(ctx, err, op, errors.WithCode(errors.Decrypt), errors.WithMsg("token"))
|
2021-06-22 22:26:26 -04:00
|
|
|
}
|
|
|
|
|
ps.Token = ptkv.Token
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ps.CtClientKey != nil && ps.ClientCert != nil {
|
|
|
|
|
type pck struct {
|
|
|
|
|
Key []byte `wrapping:"pt,key_data"`
|
|
|
|
|
CtKey []byte `wrapping:"ct,key_data"`
|
|
|
|
|
}
|
|
|
|
|
pckv := &pck{
|
|
|
|
|
CtKey: ps.CtClientKey,
|
|
|
|
|
}
|
|
|
|
|
if err := structwrapping.UnwrapStruct(ctx, cipher, pckv, nil); err != nil {
|
2021-07-29 18:52:56 -06:00
|
|
|
return errors.Wrap(ctx, err, op, errors.WithCode(errors.Decrypt), errors.WithMsg("client certificate"))
|
2021-06-22 22:26:26 -04:00
|
|
|
}
|
|
|
|
|
ps.ClientKey = pckv.Key
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-06 07:50:15 -07:00
|
|
|
func (ps *clientStore) client(ctx context.Context) (vaultClient, error) {
|
|
|
|
|
const op = "vault.(clientStore).client"
|
2021-06-22 22:26:26 -04:00
|
|
|
clientConfig := &clientConfig{
|
|
|
|
|
Addr: ps.VaultAddress,
|
|
|
|
|
Token: ps.Token,
|
|
|
|
|
CaCert: ps.CaCert,
|
|
|
|
|
TlsServerName: ps.TlsServerName,
|
|
|
|
|
TlsSkipVerify: ps.TlsSkipVerify,
|
|
|
|
|
Namespace: ps.Namespace,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ps.ClientKey != nil {
|
|
|
|
|
clientConfig.ClientCert = ps.ClientCert
|
|
|
|
|
clientConfig.ClientKey = ps.ClientKey
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-31 12:40:35 -04:00
|
|
|
client, err := vaultClientFactoryFn(ctx, clientConfig, WithWorkerFilter(ps.WorkerFilter))
|
2021-06-22 22:26:26 -04:00
|
|
|
if err != nil {
|
2023-06-06 17:09:08 -04:00
|
|
|
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create vault client"))
|
2021-06-22 22:26:26 -04:00
|
|
|
}
|
|
|
|
|
return client, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetPublicId returns the public id.
|
2022-09-06 07:50:15 -07:00
|
|
|
func (ps *clientStore) GetPublicId() string { return ps.PublicId }
|
|
|
|
|
|
|
|
|
|
// TableName returns the table name for gorm.
|
|
|
|
|
func (ps *clientStore) TableName() string {
|
|
|
|
|
return "credential_vault_store_client"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// renewRevokeStore is a clientStore that is not limited to the current token and includes
|
|
|
|
|
// 'current', 'maintaining' and 'revoke' tokens.
|
|
|
|
|
type renewRevokeStore struct {
|
|
|
|
|
Store *clientStore `gorm:"embedded"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func allocRenewRevokeStore() *renewRevokeStore {
|
|
|
|
|
return &renewRevokeStore{
|
|
|
|
|
Store: allocClientStore(),
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-06-22 22:26:26 -04:00
|
|
|
|
|
|
|
|
// TableName returns the table name for gorm.
|
2022-09-06 07:50:15 -07:00
|
|
|
func (ps *renewRevokeStore) TableName() string {
|
|
|
|
|
return "credential_vault_token_renewal_revocation"
|
2021-06-22 22:26:26 -04:00
|
|
|
}
|