diff --git a/changelog/12425.txt b/changelog/12425.txt new file mode 100644 index 000000000..709e631c6 --- /dev/null +++ b/changelog/12425.txt @@ -0,0 +1,3 @@ +```release-note:improvement +auth/approle: expose secret_id_accessor as WrappedAccessor when creating wrapped secret-id. +``` \ No newline at end of file diff --git a/sdk/helper/wrapping/wrapinfo.go b/sdk/helper/wrapping/wrapinfo.go index 9c84a1d47..8d8e63340 100644 --- a/sdk/helper/wrapping/wrapinfo.go +++ b/sdk/helper/wrapping/wrapinfo.go @@ -17,8 +17,8 @@ type ResponseWrapInfo struct { // expected expiration. CreationTime time.Time `json:"creation_time" structs:"creation_time" mapstructure:"creation_time" sentinel:""` - // If the contained response is the output of a token creation call, the - // created token's accessor will be accessible here + // If the contained response is the output of a token or approle secret-id creation call, the + // created token's/secret-id's accessor will be accessible here WrappedAccessor string `json:"wrapped_accessor" structs:"wrapped_accessor" mapstructure:"wrapped_accessor" sentinel:""` // WrappedEntityID is the entity identifier of the caller who initiated the diff --git a/vault/external_tests/approle/wrapped_secretid_test.go b/vault/external_tests/approle/wrapped_secretid_test.go new file mode 100644 index 000000000..c6665acb9 --- /dev/null +++ b/vault/external_tests/approle/wrapped_secretid_test.go @@ -0,0 +1,121 @@ +package approle + +import ( + "testing" + + log "github.com/hashicorp/go-hclog" + "github.com/hashicorp/vault/api" + credAppRole "github.com/hashicorp/vault/builtin/credential/approle" + vaulthttp "github.com/hashicorp/vault/http" + "github.com/hashicorp/vault/sdk/logical" + "github.com/hashicorp/vault/vault" + "github.com/stretchr/testify/require" +) + +func TestApproleSecretId_Wrapped(t *testing.T) { + + var err error + coreConfig := &vault.CoreConfig{ + DisableMlock: true, + DisableCache: true, + Logger: log.NewNullLogger(), + CredentialBackends: map[string]logical.Factory{ + "approle": credAppRole.Factory, + }, + } + + cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{ + HandlerFunc: vaulthttp.Handler, + }) + + cluster.Start() + defer cluster.Cleanup() + + cores := cluster.Cores + + vault.TestWaitActive(t, cores[0].Core) + + client := cores[0].Client + client.SetToken(cluster.RootToken) + + err = client.Sys().EnableAuthWithOptions("approle", &api.EnableAuthOptions{ + Type: "approle", + }) + if err != nil { + t.Fatal(err) + } + + _, err = client.Logical().Write("auth/approle/role/test-role-1", map[string]interface{}{ + "name": "test-role-1", + }) + require.NoError(t, err) + + client.SetWrappingLookupFunc(func(operation, path string) string { + return "5m" + }) + + resp, err := client.Logical().Write("/auth/approle/role/test-role-1/secret-id", map[string]interface{}{}) + require.NoError(t, err) + + wrappedAccessor := resp.WrapInfo.WrappedAccessor + wrappingToken := resp.WrapInfo.Token + + client.SetWrappingLookupFunc(func(operation, path string) string { + return api.DefaultWrappingLookupFunc(operation, path) + }) + + unwrappedSecretid, err := client.Logical().Unwrap(wrappingToken) + unwrappedAccessor := unwrappedSecretid.Data["secret_id_accessor"].(string) + + if wrappedAccessor != unwrappedAccessor { + t.Fatalf("Expected wrappedAccessor (%v) to match wrapped secret_id_accessor (%v)", wrappedAccessor, unwrappedAccessor) + } + +} + +func TestApproleSecretId_NotWrapped(t *testing.T) { + + var err error + coreConfig := &vault.CoreConfig{ + DisableMlock: true, + DisableCache: true, + Logger: log.NewNullLogger(), + CredentialBackends: map[string]logical.Factory{ + "approle": credAppRole.Factory, + }, + } + + cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{ + HandlerFunc: vaulthttp.Handler, + }) + + cluster.Start() + defer cluster.Cleanup() + + cores := cluster.Cores + + vault.TestWaitActive(t, cores[0].Core) + + client := cores[0].Client + client.SetToken(cluster.RootToken) + + err = client.Sys().EnableAuthWithOptions("approle", &api.EnableAuthOptions{ + Type: "approle", + }) + if err != nil { + t.Fatal(err) + } + + _, err = client.Logical().Write("auth/approle/role/test-role-1", map[string]interface{}{ + "name": "test-role-1", + }) + require.NoError(t, err) + + resp, err := client.Logical().Write("/auth/approle/role/test-role-1/secret-id", map[string]interface{}{}) + require.NoError(t, err) + + if resp.WrapInfo != nil && resp.WrapInfo.WrappedAccessor != "" { + t.Fatalf("WrappedAccessor unexpectedly set") + } + +} diff --git a/vault/wrapping.go b/vault/wrapping.go index 4ec02fcfe..b56b8aef1 100644 --- a/vault/wrapping.go +++ b/vault/wrapping.go @@ -184,6 +184,11 @@ DONELISTHANDLING: resp.WrapInfo.WrappedAccessor = resp.Auth.Accessor } + // Store the accessor of the approle secret in WrappedAccessor + if secretIdAccessor, ok := resp.Data["secret_id_accessor"]; ok && resp.Auth == nil && req.MountType == "approle" { + resp.WrapInfo.WrappedAccessor = secretIdAccessor.(string) + } + switch resp.WrapInfo.Format { case "jwt": // Create the JWT