Skip to content

Commit

Permalink
Merge branch 'main' into VAULT-32686-plugin-register-with-artifact-stubs
Browse files Browse the repository at this point in the history
  • Loading branch information
thyton authored Jan 8, 2025
2 parents bc13b6e + cb32dd0 commit 1eaa231
Show file tree
Hide file tree
Showing 155 changed files with 5,776 additions and 5,099 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ for write requests as a GA feature (enabled by default) for Integrated Storage.
* **Audit Entry Exclusion (enterprise)**: Audit devices support excluding fields from entries being written to them, with expression-based rules (powered by go-bexpr) to determine when the specific fields are excluded.
* **Workload Identity Federation UI for AWS (enterprise)**: Add WIF fields to AWS secrets engine. [[GH-28148](https://github.com/hashicorp/vault/pull/28148)]
* **KV v2 Patch/Subkey (enterprise)**: Adds GUI support to read the subkeys of a KV v2 secret and patch (partially update) secret data. [[GH-28212](https://github.com/hashicorp/vault/pull/28212)]
* **Self-Managed Static Roles**: Self-Managed Static Roles are now supported for select SQL database engines (Postgres, Oracle). Requires Vault Enterprise. [[GH-28199](https://github.com/hashicorp/vault/pull/28199)]
* **Self-Managed Static Roles**: Self-Managed Static Roles are now supported for the Postgres SQL database engine. Requires Vault Enterprise. [[GH-28199](https://github.com/hashicorp/vault/pull/28199)]
* **Vault Minimal Version**: Add the ability to build a minimal version of Vault
with only core features using the BUILD_MINIMAL environment variable. [[GH-27394](https://github.com/hashicorp/vault/pull/27394)]
* **Vault PKI 3GPP CMPv2 Server (Enterprise)**: Support for the PKI 3GPP CMPv2 certificate management protocol has been added to the Vault PKI Plugin. This allows standard CMPv2 clients to request certificates from a Vault server with no knowledge of Vault APIs.
Expand Down
36 changes: 16 additions & 20 deletions builtin/credential/cert/test-fixtures/keys/cert.pem
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
-----BEGIN CERTIFICATE-----
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
5serAZLPfBGztdRbTef2
-----END CERTIFICATE-----
6 changes: 6 additions & 0 deletions builtin/credential/cert/test-fixtures/keys/rebuild-cert.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
To rebuild the cert.pem within this folder run the following commands

```shell
$ openssl x509 -in cert.pem -signkey key.pem -x509toreq -out cert.csr
$ openssl x509 -req -in cert.csr -CA ../root/rootcacert.pem -CAkey ../root/rootcakey.pem -CAcreateserial -out cert.pem -days 9132 -sha256 -extensions v3_req -extfile <(echo "[v3_req]\nsubjectAltName=DNS:cert.example.com,IP:127.0.0.1")
```
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 1eaa231

Please sign in to comment.