Skip to content

Commit

Permalink
Merge branch 'main' into thyton/VAULT-33084/add-system-view-stubs
Browse files Browse the repository at this point in the history
  • Loading branch information
thyton authored Jan 10, 2025
2 parents a01edcd + ff2d7ec commit e460f9c
Show file tree
Hide file tree
Showing 132 changed files with 4,788 additions and 1,705 deletions.
2 changes: 0 additions & 2 deletions builtin/credential/ldap/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,12 @@ func (b *backend) Login(ctx context.Context, req *logical.Request, username stri
if b.Logger().IsDebug() {
b.Logger().Debug(errString)
}
ldapResponse.AddWarning(errString)
}

for _, warning := range c.Warnings {
if b.Logger().IsDebug() {
b.Logger().Debug(string(warning))
}
ldapResponse.AddWarning(string(warning))
}

var allGroups []string
Expand Down
4 changes: 2 additions & 2 deletions builtin/credential/ldap/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1183,8 +1183,8 @@ func testAccStepLoginNoGroupDN(t *testing.T, user string, pass string) logicalte

// Verifies a search without defined GroupDN returns a warning rather than failing
Check: func(resp *logical.Response) error {
if len(resp.Warnings) != 1 {
return fmt.Errorf("expected a warning due to no group dn, got: %#v", resp.Warnings)
if len(resp.Warnings) != 0 {
return fmt.Errorf("expected a no warnings, got: %#v", resp.Warnings)
}

return logicaltest.TestCheckAuth([]string{"bar", "default"})(resp)
Expand Down
87 changes: 87 additions & 0 deletions builtin/logical/aws/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"sync"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/iam"
"github.com/aws/aws-sdk-go/service/iam/iamiface"
"github.com/aws/aws-sdk-go/service/sts/stsiface"
"github.com/hashicorp/vault/sdk/framework"
Expand Down Expand Up @@ -74,6 +76,91 @@ func Backend(_ *logical.BackendConfig) *backend {
}
return nil
},
RotateCredential: func(ctx context.Context, req *logical.Request) error {
// the following code is a modified version of the rotate-root method
client, err := b.clientIAM(ctx, req.Storage)
if err != nil {
return err
}
if client == nil {
return fmt.Errorf("nil IAM client")
}

b.clientMutex.Lock()
defer b.clientMutex.Unlock()

rawRootConfig, err := req.Storage.Get(ctx, "config/root")
if err != nil {
return err
}
if rawRootConfig == nil {
return fmt.Errorf("no configuration found for config/root")
}
var config rootConfig
if err := rawRootConfig.DecodeJSON(&config); err != nil {
return fmt.Errorf("error reading root configuration: %w", err)
}

if config.AccessKey == "" || config.SecretKey == "" {
return fmt.Errorf("cannot call config/rotate-root when either access_key or secret_key is empty")
}

var getUserInput iam.GetUserInput // empty input means get current user
getUserRes, err := client.GetUserWithContext(ctx, &getUserInput)
if err != nil {
return fmt.Errorf("error calling GetUser: %w", err)
}
if getUserRes == nil {
return fmt.Errorf("nil response from GetUser")
}
if getUserRes.User == nil {
return fmt.Errorf("nil user returned from GetUser")
}
if getUserRes.User.UserName == nil {
return fmt.Errorf("nil UserName returned from GetUser")
}

createAccessKeyInput := iam.CreateAccessKeyInput{
UserName: getUserRes.User.UserName,
}
createAccessKeyRes, err := client.CreateAccessKeyWithContext(ctx, &createAccessKeyInput)
if err != nil {
return fmt.Errorf("error calling CreateAccessKey: %w", err)
}
if createAccessKeyRes.AccessKey == nil {
return fmt.Errorf("nil response from CreateAccessKey")
}
if createAccessKeyRes.AccessKey.AccessKeyId == nil || createAccessKeyRes.AccessKey.SecretAccessKey == nil {
return fmt.Errorf("nil AccessKeyId or SecretAccessKey returned from CreateAccessKey")
}

oldAccessKey := config.AccessKey

config.AccessKey = *createAccessKeyRes.AccessKey.AccessKeyId
config.SecretKey = *createAccessKeyRes.AccessKey.SecretAccessKey

newEntry, err := logical.StorageEntryJSON("config/root", config)
if err != nil {
return fmt.Errorf("error generating new config/root JSON: %w", err)
}
if err := req.Storage.Put(ctx, newEntry); err != nil {
return fmt.Errorf("error saving new config/root: %w", err)
}

b.iamClient = nil
b.stsClient = nil

deleteAccessKeyInput := iam.DeleteAccessKeyInput{
AccessKeyId: aws.String(oldAccessKey),
UserName: getUserRes.User.UserName,
}
_, err = client.DeleteAccessKeyWithContext(ctx, &deleteAccessKeyInput)
if err != nil {
return fmt.Errorf("error deleting old access key: %w", err)
}

return nil
},
BackendType: logical.TypeLogical,
}

Expand Down
111 changes: 98 additions & 13 deletions builtin/logical/aws/path_config_root.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,18 @@ import (

"github.com/aws/aws-sdk-go/aws"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/helper/automatedrotationutil"
"github.com/hashicorp/vault/sdk/helper/pluginidentityutil"
"github.com/hashicorp/vault/sdk/helper/pluginutil"
"github.com/hashicorp/vault/sdk/logical"
"github.com/hashicorp/vault/sdk/rotation"
)

// A single default template that supports both the different credential types (IAM/STS) that are capped at differing length limits (64 chars/32 chars respectively)
const defaultUserNameTemplate = `{{ if (eq .Type "STS") }}{{ printf "vault-%s-%s" (unix_time) (random 20) | truncate 32 }}{{ else }}{{ printf "vault-%s-%s-%s" (printf "%s-%s" (.DisplayName) (.PolicyName) | truncate 42) (unix_time) (random 20) | truncate 64 }}{{ end }}`
const (
defaultUserNameTemplate = `{{ if (eq .Type "STS") }}{{ printf "vault-%s-%s" (unix_time) (random 20) | truncate 32 }}{{ else }}{{ printf "vault-%s-%s-%s" (printf "%s-%s" (.DisplayName) (.PolicyName) | truncate 42) (unix_time) (random 20) | truncate 64 }}{{ end }}`
rootRotationJobName = "aws-root-creds"
)

func pathConfigRoot(b *backend) *framework.Path {
p := &framework.Path{
Expand Down Expand Up @@ -95,6 +100,7 @@ func pathConfigRoot(b *backend) *framework.Path {
HelpDescription: pathConfigRootHelpDesc,
}
pluginidentityutil.AddPluginIdentityTokenFields(p.Fields)
automatedrotationutil.AddAutomatedRotationFields(p.Fields)

return p
}
Expand All @@ -103,20 +109,14 @@ func (b *backend) pathConfigRootRead(ctx context.Context, req *logical.Request,
b.clientMutex.RLock()
defer b.clientMutex.RUnlock()

entry, err := req.Storage.Get(ctx, "config/root")
config, exists, err := getConfigFromStorage(ctx, req)
if err != nil {
return nil, err
}
if entry == nil {
if !exists {
return nil, nil
}

var config rootConfig

if err := entry.DecodeJSON(&config); err != nil {
return nil, err
}

configData := map[string]interface{}{
"access_key": config.AccessKey,
"region": config.Region,
Expand All @@ -131,6 +131,8 @@ func (b *backend) pathConfigRootRead(ctx context.Context, req *logical.Request,
}

config.PopulatePluginIdentityTokenData(configData)
config.PopulateAutomatedRotationData(configData)

return &logical.Response{
Data: configData,
}, nil
Expand Down Expand Up @@ -158,6 +160,12 @@ func (b *backend) pathConfigRootWrite(ctx context.Context, req *logical.Request,
b.clientMutex.Lock()
defer b.clientMutex.Unlock()

// check for existing config
previousCfg, previousCfgExists, err := getConfigFromStorage(ctx, req)
if err != nil {
return nil, err
}

rc := rootConfig{
AccessKey: data.Get("access_key").(string),
SecretKey: data.Get("secret_key").(string),
Expand All @@ -174,6 +182,9 @@ func (b *backend) pathConfigRootWrite(ctx context.Context, req *logical.Request,
if err := rc.ParsePluginIdentityTokenFields(data); err != nil {
return logical.ErrorResponse(err.Error()), nil
}
if err := rc.ParseAutomatedRotationFields(data); err != nil {
return logical.ErrorResponse(err.Error()), nil
}

if rc.IdentityTokenAudience != "" && rc.AccessKey != "" {
return logical.ErrorResponse("only one of 'access_key' or 'identity_token_audience' can be set"), nil
Expand All @@ -195,12 +206,54 @@ func (b *backend) pathConfigRootWrite(ctx context.Context, req *logical.Request,
}
}

entry, err := logical.StorageEntryJSON("config/root", rc)
if err != nil {
return nil, err
// Save the initial config only if it does not already exist
if !previousCfgExists {
if err := putConfigToStorage(ctx, req, &rc); err != nil {
return nil, err
}
}

if err := req.Storage.Put(ctx, entry); err != nil {
// Now that the root config is set up, register the rotation job if it required
if rc.ShouldRegisterRotationJob() {
cfgReq := &rotation.RotationJobConfigureRequest{
Name: rootRotationJobName,
MountPoint: req.MountPoint,
ReqPath: req.Path,
RotationSchedule: rc.RotationSchedule,
RotationWindow: rc.RotationWindow,
RotationPeriod: rc.RotationPeriod,
}

rotationJob, err := rotation.ConfigureRotationJob(cfgReq)
if err != nil {
return logical.ErrorResponse("error configuring rotation job: %s", err), nil
}

b.Logger().Debug("Registering rotation job", "mount", req.MountPoint+req.Path)
rotationID, err := b.System().RegisterRotationJob(ctx, rotationJob)
if err != nil {
return logical.ErrorResponse("error registering rotation job: %s", err), nil
}

rc.RotationID = rotationID
}

// Disable Automated Rotation and Deregister credentials if required
if rc.DisableAutomatedRotation {
// Ensure de-registering only occurs on updates and if
// a credential has actually been registered
if previousCfgExists && previousCfg.RotationID != "" {
err := b.System().DeregisterRotationJob(ctx, previousCfg.RotationID)
if err != nil {
return logical.ErrorResponse("error de-registering rotation job: %s", err), nil
}

rc.RotationID = ""
}
}

// update config entry with rotation ID
if err := putConfigToStorage(ctx, req, &rc); err != nil {
return nil, err
}

Expand All @@ -212,8 +265,40 @@ func (b *backend) pathConfigRootWrite(ctx context.Context, req *logical.Request,
return nil, nil
}

func getConfigFromStorage(ctx context.Context, req *logical.Request) (*rootConfig, bool, error) {
entry, err := req.Storage.Get(ctx, "config/root")
if err != nil {
return nil, false, err
}
if entry == nil {
return nil, false, nil
}

var config rootConfig

if err := entry.DecodeJSON(&config); err != nil {
return nil, false, err
}

return &config, true, nil
}

func putConfigToStorage(ctx context.Context, req *logical.Request, rc *rootConfig) error {
entry, err := logical.StorageEntryJSON("config/root", rc)
if err != nil {
return err
}

if err := req.Storage.Put(ctx, entry); err != nil {
return err
}

return nil
}

type rootConfig struct {
pluginidentityutil.PluginIdentityTokenParams
automatedrotationutil.AutomatedRotationParams

AccessKey string `json:"access_key"`
SecretKey string `json:"secret_key"`
Expand Down
Loading

0 comments on commit e460f9c

Please sign in to comment.