HashiCorp Vault Incorrectly Validated JSON Web Tokens (JWT) Audience Claims
Research is free — Hunters explains how the bug works, the root-cause code pattern, how the fix addresses it, and how to test whether a target is affected, in chat. Investigate & write exploit is a paid run — the engine reads the advisory and fix commits, then builds and validates a working proof-of-concept exploit with reproduction steps.
Affected versions
Details
Vault and Vault Enterprise did not properly validate the JSON Web Token (JWT) role-bound audience claim when using the Vault JWT auth method. This may have resulted in Vault validating a JWT the audience and role-bound claims do not match, allowing an invalid login to succeed when it should have been rejected. This vulnerability, CVE-2024-5798, was fixed in Vault and Vault Enterprise 1.17.0, 1.16.3, and 1.15.9
The fix
Release delta 1.17.0-rc1 → 1.17.0 (contains the fix)
website/content/docs/auth/gcp.mdx+91 −1
@@ -100,6 +100,41 @@ management tool.environment, you will additionally need to configure your environment’s custom endpointsvia the [custom_endpoint](/vault/api-docs/auth/gcp#custom_endpoint) configuration parameter.+In some cases, you cannot set sensitive IAM security credentials in your+Vault configuration. For example, your organization may require that all+security credentials are short-lived or explicitly tied to a machine identity.++To provide IAM security credentials to Vault, we recommend using Vault+[plugin workload identity federation](#plugin-workload-identity-federation-wif)+(WIF) as shown below.++1. Alternatively, configure the audience claim value and the service account email to assume for plugin workload identity federation:++```text+$ vault write auth/gcp/config \+identity_token_audience="<TOKEN AUDIENCE>" \+service_account_email="<SERVICE ACCOUNT EMAIL>"+```++Vault's identity token provider signs the plugin identity token JWT internally.+If a trust relationship exists between Vault and GCP through WIF, the auth+method can exchange the Vault identity token for a+[federated access token](https://cloud.google.com/docs/authentication/token-types#access).++To configure a trusted relationship between Vault and GCP:+- You must configure the [identity token issuer backend](/vault/api-docs/secret/identity/tokens#configure-the-identity-tokens-backend)+for Vault.+- GCP must have a+[workload identity pool and provider](https://cloud.google.com/iam/docs/manage-workload-identity-pools-providers)+configured with information about the fully qualified and network-reachable+issuer URL for the Vault plugin's+[identity token provider](/vault/api-docs/secret/identity/tokens#read-plugin-identity-well-known-configurations).++Establishing a trusted relationship between Vault and GCP ensures that GCP+can fetch JWKS+[public keys](/vault/api-docs/secret/identity/tokens#read-active-public-keys)+and verify the plugin identity token signature.+1. Create a named role:For an `iam`-type role:@@ -224,6 +259,61 @@ account to impersonate any service account in the GCP project where it resides.See [Managing service account impersonation](https://cloud.google.com/iam/docs/impersonating-service-accounts)for more information.+## Plugin Workload Identity Federation (WIF)++<EnterpriseAlert product="vault" />++The GCP auth method supports the plugin WIF workflow and has a source of identity called+a plugin identity token. A plugin identity token is a JWT that is signed internally by the Vault+[plugin identity token issuer](/vault/api-docs/secret/identity/tokens#read-plugin-workload-identity-issuer-s-openid-configuration).++If there is a trust relationship configured between Vault and GCP through+[workload identity federation](https://cloud.google.com/iam/docs/workload-identity-federation),+the auth method can exchange its identity token for short-lived access tokens needed to+perform its actions.++Exchanging identity tokens for access tokens lets the GCP auth method+operate without configuring explicit access to sensitive IAM security+credentials.++To configure the auth method to use plugin WIF:++1. Ensure that Vault [openid-configuration](/vault/api-docs/secret/identity/tokens#read-plugin-identity-token-issuer-s-openid-configuration)+and [public JWKS](/vault/api-docs/secret/identity/tokens#read-plugin-identity-token-issuer-s-public-jwks)+APIs are network-reachable by GCP. We recommend using an API proxy or gateway+if you need to limit Vault API exposure.++1. Create a+[workload identity pool and provider](https://cloud.google.com/iam/docs/workload-identity-federation-with-other-providers#create-pool-provider)+in GCP.+1. The provider URL **must** point at your [Vault plugin identity token issuer](/vault/api-docs/secret/identity/tokens#read-plugin-workload-identity-issuer-s-openid-configuration) with the+`/.well-known/openid-configuration` suffix removed. For example:+`https://host:port/v1/identity/oidc/plugins`.+1. Uniquely identify the recipient of the plugin identity token as the audience.+You can use the [default audience](https://cloud.google.com/iam/docs/workload-identity-federation-with-other-providers#prepare)+for the identity pool or a custom value less than 256 characters.++1. [Authenticate a workload](https://cloud.google.com/iam/docs/workload-identity-federation-with-other-providers#authenticate)+in GCP by granting the identity pool access to a dedicated service account using service account impersonation.+Filter requests using the unique `sub` claim issued by plugin identity tokens so the GCP Auth method can+impersonate the service account. `sub` claims have the form: `plugin-identity:<NAMESPACE>:auth:<GCP_AUTH_MOUNT_ACCESSOR>`.++1. Configure the GCP auth method with the OIDC audience value and service account+email.++```shell-session+$ vault write auth/gcp/config \+identity_token_audience="//iam.googleapis.com/projects/410449834127/locations/global/workloadIdentityPools/vault-gcp-auth-43777a63/providers/vault-gcp-auth-wif-provider" \+service_account_email="vault-plugin-wif-auth@hc-b712f250b4e04cacbadd258a90b.iam.gserviceaccount.com"+```++Your auth method can now use plugin WIF for its configuration credentials.+By default, WIF [credentials](https://cloud.google.com/iam/docs/workload-identity-federation#access_management)+have a time-to-live of 1 hour and automatically refresh when they expire.++Please see the [API documentation](/vault/api-docs/auth/gcp#configure)+for more details on the fields associated with plugin WIF.+## Group aliasesAs of Vault 1.0, roles can specify an `add_group_aliases` boolean parameter@@ -275,7 +365,7 @@ for IAM service accounts looks like this:### GCE loginGCE login only applies to roles of type `gce` and **must be completed on an-infrastructure running on Google Cloud**. These steps will not work from your+infrastructure running on Google Cloud**. These steps will not work from yourlocal laptop or another cloud provider.[](/img/vault-gcp-gce-auth-workflow.svg)
ui/tests/acceptance/tools-test.js+100 −55
@@ -21,6 +21,8 @@ import authPage from 'vault/tests/pages/auth';import { capitalize } from '@ember/string';import codemirror from 'vault/tests/helpers/codemirror';import { setupMirage } from 'ember-cli-mirage/test-support';+import { GENERAL } from 'vault/tests/helpers/general-selectors';+import { TOOLS_SELECTORS as TS } from 'vault/tests/helpers/tools-selectors';module('Acceptance | tools', function (hooks) {setupApplicationTest(hooks);@@ -33,13 +35,6 @@ module('Acceptance | tools', function (hooks) {const DATA_TO_WRAP = JSON.stringify({ tools: 'tests' });const TOOLS_ACTIONS = toolsActions();-/*-data-test-tools-input="wrapping-token"-data-test-tools-input="rewrapped-token"-data-test-tools="token-lookup-row"-data-test-sidebar-nav-link=supportedAction-*/-var createTokenStore = () => {let token;return {@@ -51,92 +46,89 @@ module('Acceptance | tools', function (hooks) {},};};+test('tools functionality', async function (assert) {var tokenStore = createTokenStore();await visit('/vault/tools');assert.strictEqual(currentURL(), '/vault/tools/wrap', 'forwards to the first action');TOOLS_ACTIONS.forEach((action) => {-assert.dom(`[data-test-sidebar-nav-link="${capitalize(action)}"]`).exists(`${action} link renders`);+assert.dom(GENERAL.navLink(capitalize(action))).exists(`${action} link renders`);});await waitFor('.CodeMirror');codemirror().setValue(DATA_TO_WRAP);// wrap-await click('[data-test-tools-submit]');-const wrappedToken = await waitUntil(() => find('[data-test-tools-input="wrapping-token"]'));-tokenStore.set(wrappedToken.value);-assert-.dom('[data-test-tools-input="wrapping-token"]')-.hasValue(wrappedToken.value, 'has a wrapping token');--//lookup-await click('[data-test-sidebar-nav-link="Lookup"]');--await fillIn('[data-test-tools-input="wrapping-token"]', tokenStore.get());-await click('[data-test-tools-submit]');-await waitUntil(() => findAll('[data-test-tools="token-lookup-row"]').length >= 3);-const rows = findAll('[data-test-tools="token-lookup-row"]');-assert.dom(rows[0]).hasText(/Creation path/, 'show creation path row');-assert.dom(rows[1]).hasText(/Creation time/, 'show creation time row');-assert.dom(rows[2]).hasText(/Creation TTL/, 'show creation ttl row');--//rewrap-await click('[data-test-sidebar-nav-link="Rewrap"]');--await fillIn('[data-test-tools-input="wrapping-token"]', tokenStore.get());-await click('[data-test-tools-submit]');-const rewrappedToken = await waitUntil(() => find('[data-test-tools-input="rewrapped-token"]'));+await click(TS.submit);+const wrappedToken = await waitUntil(() => find(TS.toolsInput('wrapping-token')));+tokenStore.set(wrappedToken.innerText);++// lookup+await click(GENERAL.navLink('Lookup'));++await fillIn(TS.toolsInput('wrapping-token'), tokenStore.get());+await click(TS.submit);+await waitUntil(() => findAll('[data-test-component="info-table-row"]').length >= 3);+assert.dom(GENERAL.infoRowValue('Creation path')).hasText('sys/wrapping/wrap', 'show creation path row');+assert.dom(GENERAL.infoRowValue('Creation time')).exists();+assert.dom(GENERAL.infoRowValue('Creation TTL')).hasText('1800', 'show creation ttl row');++// rewrap+await click(GENERAL.navLink('Rewrap'));++await fillIn(TS.toolsInput('wrapping-token'), tokenStore.get());+await click(TS.submit);+const rewrappedToken = await waitUntil(() => find(TS.toolsInput('rewrapped-token')));assert.ok(rewrappedToken.value, 'has a new re-wrapped token');assert.notEqual(rewrappedToken.value, tokenStore.get(), 're-wrapped token is not the wrapped token');tokenStore.set(rewrappedToken.value);await settled();-//unwrap-await click('[data-test-sidebar-nav-link="Unwrap"]');+// unwrap+await click(GENERAL.navLink('Unwrap'));-await fillIn('[data-test-tools-input="wrapping-token"]', tokenStore.get());-await click('[data-test-tools-submit]');+await fillIn(TS.toolsInput('wrapping-token'), tokenStore.get());+await click(TS.submit);await waitFor('.CodeMirror');assert.deepEqual(JSON.parse(codemirror().getValue()),JSON.parse(DATA_TO_WRAP),'unwrapped data equals input data');-await waitUntil(() => find('[data-test-button-details]'));-await click('[data-test-button-details]');-await click('[data-test-button-data]');+await waitUntil(() => find(TS.tab('details')));+await click(TS.tab('details'));+await click(TS.tab('data'));assert.deepEqual(JSON.parse(codemirror().getValue()),JSON.parse(DATA_TO_WRAP),'data tab still has unwrapped data');//random-await click('[data-test-sidebar-nav-link="Random"]');+await click(GENERAL.navLink('Random'));-assert.dom('[data-test-tools-input="bytes"]').hasValue('32', 'defaults to 32 bytes');-await click('[data-test-tools-submit]');-const randomBytes = await waitUntil(() => find('[data-test-tools-input="random-bytes"]'));+assert.dom(TS.toolsInput('bytes')).hasValue('32', 'defaults to 32 bytes');+await click(TS.submit);+const randomBytes = await waitUntil(() => find(TS.toolsInput('random-bytes')));assert.ok(randomBytes.value, 'shows the returned value of random bytes');-//hash-await click('[data-test-sidebar-nav-link="Hash"]');+// hash+await click(GENERAL.navLink('Hash'));-await fillIn('[data-test-tools-input="hash-input"]', 'foo');+await fillIn(TS.toolsInput('hash-input'), 'foo');await click('[data-test-transit-b64-toggle="input"]');-await click('[data-test-tools-submit]');-let sumInput = await waitUntil(() => find('[data-test-tools-input="sum"]'));+await click(TS.submit);+let sumInput = await waitUntil(() => find(TS.toolsInput('sum')));assert.dom(sumInput).hasValue('LCa0a2j/xo/5m0U8HTBBNBNCLXBkg7+g+YpeiGJm564=', 'hashes the data, encodes input');-await click('[data-test-tools-back]');+await click(TS.button('Back'));-await fillIn('[data-test-tools-input="hash-input"]', 'e2RhdGE6ImZvbyJ9');+await fillIn(TS.toolsInput('hash-input'), 'e2RhdGE6ImZvbyJ9');-await click('[data-test-tools-submit]');-sumInput = await waitUntil(() => find('[data-test-tools-input="sum"]'));+await click(TS.submit);+sumInput = await waitUntil(() => find(TS.toolsInput('sum')));assert.dom(sumInput).hasValue('JmSi2Hhbgu2WYOrcOyTqqMdym7KT3sohCwAwaMonVrc=', 'hashes the data, passes b64 input through');@@ -168,10 +160,10 @@ module('Acceptance | tools', function (hooks) {await visit('/vault/tools');//unwrap-await click('[data-test-sidebar-nav-link="Unwrap"]');+await click(GENERAL.navLink('Unwrap'));-await fillIn('[data-test-tools-input="wrapping-token"]', 'sometoken');-await click('[data-test-tools-submit]');+await fillIn(TS.toolsInput('wrapping-token'), 'sometoken');+await click(TS.submit);await waitFor('.CodeMirror');assert.deepEqual(@@ -180,4 +172,57 @@ module('Acceptance | tools', function (hooks) {'unwrapped data equals input data');});++module('wrap', function () {+test('it wraps data again after clicking "Back"', async function (assert) {+const tokenStore = createTokenStore();+await visit('/vault/tools/wrap');++await waitFor('.CodeMirror');+codemirror().setValue(DATA_TO_WRAP);++// initial wrap+await click(TS.submit);+await waitUntil(() => find(TS.toolsInput('wrapping-token')));+await click(TS.button('Back'));++// wrap again+await click(TS.submit);+const wrappedToken = await waitUntil(() => find(TS.toolsInput('wrapping-token')));+tokenStore.set(wrappedToken.innerText);++// there was a bug where clicking "back" cleared the parent's data, but not the child form component+// so when users attempted to wrap data again the payload was actually empty and unwrapping the token returned {token: ""}+// it is user desired behavior that the form does not clear on back, and that wrapping can be immediately repeated+// we use lookup to check our token from the second wrap returns the unwrapped data we expect+await click(GENERAL.navLink('Lookup'));+await fillIn(TS.toolsInput('wrapping-token'), tokenStore.get());+await click(TS.submit);+await waitUntil(() => findAll('[data-test-component="info-table-row"]').length >= 3);+assert.dom(GENERAL.infoRowValue('Creation TTL')).hasText('1800', 'show creation ttl row');+});++test('it sends wrap ttl', async function (assert) {+const tokenStore = createTokenStore();+await visit('/vault/tools/wrap');++await waitFor('.CodeMirror');+codemirror().setValue(DATA_TO_WRAP);++// update to non-default ttl+await click(GENERAL.toggleInput('Wrap TTL'));+await fillIn(GENERAL.ttl.input('Wrap TTL'), '20');++await click(TS.submit);+const wrappedToken = await waitUntil(() => find(TS.toolsInput('wrapping-token')));+tokenStore.set(wrappedToken.innerText);++// lookup to check unwrapped data is what we expect+await click(GENERAL.navLink('Lookup'));+await fillIn(TS.toolsInput('wrapping-token'), tokenStore.get());+await click(TS.submit);+await waitUntil(() => findAll('[data-test-component="info-table-row"]').length >= 3);+assert.dom(GENERAL.infoRowValue('Creation TTL')).hasText('1200', 'show creation ttl row');+});+});});
website/content/docs/secrets/gcp.mdx+97 −6
@@ -62,6 +62,42 @@ management tool.place of specifying the credentials JSON file.For more information on authentication, see the [authentication section](#authentication) below.+In some cases, you cannot set sensitive IAM security credentials in your+Vault configuration. For example, your organization may require that all+security credentials are short-lived or explicitly tied to a machine identity.++To provide IAM security credentials to Vault, we recommend using Vault+[plugin workload identity federation](#plugin-workload-identity-federation-wif)+(WIF) as shown below.+++1. Alternatively, configure the audience claim value and the service account email to assume for plugin workload identity federation:++```text+$ vault write gcp/config \+identity_token_audience="<TOKEN AUDIENCE>" \+service_account_email="<SERVICE ACCOUNT EMAIL>"+```++Vault's identity token provider signs the plugin identity token JWT internally.+If a trust relationship exists between Vault and GCP through WIF, the secrets+engine can exchange the Vault identity token for a+[federated access token](https://cloud.google.com/docs/authentication/token-types#access).++To configure a trusted relationship between Vault and GCP:+- You must configure the [identity token issuer backend](/vault/api-docs/secret/identity/tokens#configure-the-identity-tokens-backend)+for Vault.+- GCP must have a+[workload identity pool and provider](https://cloud.google.com/iam/docs/manage-workload-identity-pools-providers)+configured with information about the fully qualified and network-reachable+issuer URL for the Vault plugin's+[identity token provider](/vault/api-docs/secret/identity/tokens#read-plugin-identity-well-known-configurations).++Establishing a trusted relationship between Vault and GCP ensures that GCP+can fetch JWKS+[public keys](/vault/api-docs/secret/identity/tokens#read-active-public-keys)+and verify the plugin identity token signature.+1. Configure rolesets or static accounts. See the relevant sections below.## Rolesets@@ -77,11 +113,11 @@ For more information on the differences between rolesets and static accounts, se### Roleset policy considerations-Starting with Vault 1.8.0, existing permissive policies containing globs-for the GCP Secrets Engine may grant additional privileges due to the introduction+Starting with Vault 1.8.0, existing permissive policies containing globs+for the GCP Secrets Engine may grant additional privileges due to the introductionof `/gcp/roleset/:roleset/token` and `/gcp/roleset/:roleset/key` endpoints.-The following policy grants a user the ability to read all rolesets, but would+The following policy grants a user the ability to read all rolesets, but wouldalso allow them to generate tokens and keys. This type of policy is not recommended:```hcl@@ -91,7 +127,7 @@ path "/gcp/roleset/*" {}```-The following example demonstrates how a wildcard can instead be used in a roleset policy to+The following example demonstrates how a wildcard can instead be used in a roleset policy toadhere to the principle of least privilege:```hcl@@ -100,7 +136,7 @@ path "/gcp/roleset/+" {}```-For more more information on policy syntax, see the+For more more information on policy syntax, see the[policy documentation](/vault/docs/concepts/policies#policy-syntax).### Examples@@ -217,7 +253,7 @@ Impersonated accounts are a way to generate an OAuth2 [access token](/vault/docsthe permissions and accesses of another given service account. These accesstokens do not have the same 10-key limit as service account keys do, yet theyretain their short-lived nature. By default, their TTL in GCP is 1 hour, but-this may be configured to be up to 12 hours as explained in Google's+this may be configured to be up to 12 hours as explained in Google's[short-lived credentials documentation](https://cloud.google.com/iam/docs/create-short-lived-credentials-delegated#sa-credentials-oauth).For more information regarding service account impersonation in GCP, consider starting@@ -546,6 +582,61 @@ You can either:This means to update access on the dataset, Vault must be able to update the dataset'smetadata.+## Plugin Workload Identity Federation (WIF)++<EnterpriseAlert product="vault" />++The GCP secrets engine supports the plugin WIF workflow and has a source of identity called+a plugin identity token. The plugin identity token is a JWT that is signed internally by Vault's+[plugin identity token issuer](/vault/api-docs/secret/identity/tokens#read-plugin-workload-identity-issuer-s-openid-configuration).++If there is a trust relationship configured between Vault and GCP through+[workload identity federation](https://cloud.google.com/iam/docs/workload-identity-federation),+the secrets engine can exchange its identity token for short-lived access tokens needed to+perform its actions.++Exchanging identity tokens for access tokens lets the GCP secrets engine+operate without configuring explicit access to sensitive IAM security+credentials.++To configure the secrets engine to use plugin WIF:++1. Ensure that Vault [openid-configuration](/vault/api-docs/secret/identity/tokens#read-plugin-identity-token-issuer-s-openid-configuration)+and [public JWKS](/vault/api-docs/secret/identity/tokens#read-plugin-identity-token-issuer-s-public-jwks)+APIs are network-reachable by GCP. We recommend using an API proxy or gateway+if you need to limit Vault API exposure.++1. Create a+[workload identity pool and provider](https://cloud.google.com/iam/docs/workload-identity-federation-with-other-providers#create-pool-provider)+in GCP.+1. The provider URL **must** point at your [Vault plugin identity token issuer](/vault/api-docs/secret/identity/tokens#read-plugin-workload-identity-issuer-s-openid-configuration) with the+`/.well-known/openid-configuration` suffix removed. For example:+`https://host:port/v1/identity/oidc/plugins`.+1. Uniquely identify the recipient of the plugin identity token as the audience.+You can use the [default audience](https://cloud.google.com/iam/docs/workload-identity-federation-with-other-providers#prepare)+for the identity pool or a custom value less than 256 characters.++1. [Authenticate a workload](https://cloud.google.com/iam/docs/workload-identity-federation-with-other-providers#authenticate)+in GCP by granting the identity pool access to a dedicated service account using service account impersonation.+Filter requests using the unique `sub` claim issued by plugin identity tokens so the GCP Auth engine can+impersonate the service account. `sub` claims have the form: `plugin-identity:<NAMESPACE>:secret:<GCP_SECRETS_MOUNT_ACCESSOR>`.++1. Configure the GCP secrets engine with the OIDC audience value and service account+email.++```shell-session+$ vault write gcp/config \+identity_token_audience="//iam.googleapis.com/projects/410449834127/locations/global/workloadIdentityPools/vault-gcp-secrets-43777a63/providers/vault-gcp-secrets-wif-provider" \+service_account_email="vault-plugin-wif-secrets@hc-b712f250b4e04cacbadd258a90b.iam.gserviceaccount.com"+```++Your secrets engine can now use plugin WIF for its configuration credentials.+By default, WIF [credentials](https://cloud.google.com/iam/docs/workload-identity-federation#access_management)+have a time-to-live of 1 hour and automatically refresh when they expire.++Please see the [API documentation](/vault/api-docs/secret/gcp#write-config)+for more details on the fields associated with plugin WIF.+### Root credential rotationIf the mount is configured with credentials directly, the credential's key may be<82990506+hc-github-team-secure-vault-core@users.noreply.github.com>1f64e6e9ca9cba5bbe98539925f3baa0cae03dec (#27341)changelog/27289.txt | 3 +ui/app/components/tool-actions-form.js | 23 +--ui/app/components/tool-wrap.js | 37 ++---.../components/tool-actions-form.hbs | 7 +-ui/app/templates/components/tool-hash.hbs | 4 +-ui/app/templates/components/tool-lookup.hbs | 12 +-ui/app/templates/components/tool-random.hbs | 2 +-ui/app/templates/components/tool-rewrap.hbs | 2 +-ui/app/templates/components/tool-unwrap.hbs | 6 +-ui/app/templates/components/tool-wrap.hbs | 37 ++---ui/tests/acceptance/tools-test.js | 155 +++++++++++-------ui/tests/helpers/tools-selectors.ts | 11 ++.../components/tools/tool-wrap-test.js | 81 +++++++++13 files changed, 253 insertions(+), 127 deletions(-)create mode 100644 changelog/27289.txtcreate mode 100644 ui/tests/helpers/tools-selectors.tscreate mode 100644 ui/tests/integration/components/tools/tool-wrap-test.js
website/content/docs/auth/azure.mdx+84 −0
@@ -151,6 +151,41 @@ tool.For the complete list of configuration options, please see the APIdocumentation.+In some cases, you cannot set sensitive account credentials in your+Vault configuration. For example, your organization may require that all+security credentials are short-lived or explicitly tied to a machine identity.++To provide managed identity security credentials to Vault, we recommend using Vault+[plugin workload identity federation](#plugin-workload-identity-federation-wif)+(WIF) as shown below.++1. Alternatively, configure the audience claim value and the Client, Tenant IDs for plugin workload identity federation:++```text+$ vault write azure/config \+tenant_id=7cd1f227-ca67-4fc6-a1a4-9888ea7f388c \+client_id=dd794de4-4c6c-40b3-a930-d84cd32e9699 \+identity_token_audience=vault.example/v1/identity/oidc/plugins+```++The Vault identity token provider signs the plugin identity token JWT internally.+If a trust relationship exists between Vault and Azure through WIF, the auth+method can exchange the Vault identity token for a federated access token.++To configure a trusted relationship between Vault and Azure:+- You must configure the [identity token issuer backend](/vault/api-docs/secret/identity/tokens#configure-the-identity-tokens-backend)+for Vault.+- Azure must have a+[federated identity credential](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-create-trust?pivots=identity-wif-apps-methods-azp#configure-a-federated-identity-credential-on-an-app)+configured with information about the fully qualified and network-reachable+issuer URL for the Vault plugin+[identity token provider](/vault/api-docs/secret/identity/tokens#read-plugin-identity-well-known-configurations).++Establishing a trusted relationship between Vault and Azure ensures that Azure+can fetch JWKS+[public keys](/vault/api-docs/secret/identity/tokens#read-active-public-keys)+and verify the plugin identity token signature.+1. Create a role:```shell-session@@ -229,6 +264,55 @@ server:AZURE_GO_SDK_LOG_LEVEL=DEBUG```+## Plugin Workload Identity Federation (WIF)++<EnterpriseAlert product="vault" />++The Azure auth method supports the plugin WIF workflow, and has a source of identity called+a plugin identity token. A plugin identity token is a JWT that is signed internally by Vault's+[plugin identity token issuer](/vault/api-docs/secret/identity/tokens#read-plugin-workload-identity-issuer-s-openid-configuration).++If there is a trust relationship configured between Vault and Azure through+[workload identity federation](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation),+the auth method can exchange its identity token for short-lived access tokens needed to+perform its actions.++Exchanging identity tokens for access tokens lets the Azure auth method+operate without configuring explicit access to sensitive client credentials.++To configure the auth method to use plugin WIF:++1. Ensure that Vault [openid-configuration](/vault/api-docs/secret/identity/tokens#read-plugin-identity-token-issuer-s-openid-configuration)+and [public JWKS](/vault/api-docs/secret/identity/tokens#read-plugin-identity-token-issuer-s-public-jwks)+APIs are network-reachable by Azure. We recommend using an API proxy or gateway+if you need to limit Vault API exposure.++1. Configure a+[federated identity credential](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-create-trust?pivots=identity-wif-apps-methods-azp#configure-a-federated-identity-credential-on-an-app)+on a dedicated application registration in Azure to establish a trust relationship with Vault.+1. The issuer URL **must** point at your [Vault plugin identity token issuer](/vault/api-docs/secret/identity/tokens#read-plugin-workload-identity-issuer-s-openid-configuration) with the+`/.well-known/openid-configuration` suffix removed. For example:+`https://host:port/v1/identity/oidc/plugins`.+1. The subject identifier **must** match the unique `sub` claim issued by plugin identity tokens.+The subject identifier should have the form `plugin-identity:<NAMESPACE>:auth:<AZURE_MOUNT_ACCESSOR>`.+1. The audience should be under 600 characters. The default value in Azure is `api://AzureADTokenExchange`.++1. Configure the Azure auth method with the client and tenant IDs and the OIDC audience value.++```shell-session+$ vault write azure/config \+tenant_id=7cd1f227-ca67-4fc6-a1a4-9888ea7f388c \+client_id=dd794de4-4c6c-40b3-a930-d84cd32e9699 \+identity_token_audience=vault.example/v1/identity/oidc/plugins+```++Your auth method can now use plugin WIF for its configuration credentials.+By default, WIF [credentials](https://learn.microsoft.com/en-us/entra/identity-platform/access-tokens#token-lifetime)+have a time-to-live of 1 hour and automatically refresh when they expire.++Please see the [API documentation](/vault/api-docs/auth/azure#configure)+for more details on the fields associated with plugin WIF.+## APIThe Azure Auth Plugin has a full HTTP API. Please see the [API documentation](/vault/api-docs/auth/azure) for more details.
website/content/docs/secrets/azure.mdx+86 −0
@@ -61,6 +61,42 @@ management tool.If you are running Vault inside an Azure VM with MSI enabled, `client_id` and`client_secret` may be omitted. For more information on authentication, see the [authentication](#authentication) section below.+In some cases, you cannot set sensitive account credentials in your+Vault configuration. For example, your organization may require that all+security credentials are short-lived or explicitly tied to a machine identity.++To provide managed identity security credentials to Vault, we recommend using Vault+[plugin workload identity federation](#plugin-workload-identity-federation-wif)+(WIF) as shown below.++1. Alternatively, configure the audience claim value and the Client, Tenant and Subscription IDs for plugin workload identity federation:++```text+$ vault write azure/config \+subscription_id=$AZURE_SUBSCRIPTION_ID \+tenant_id=$AZURE_TENANT_ID \+client_id=$AZURE_CLIENT_ID \+identity_token_audience=$TOKEN_AUDIENCE+```++The Vault identity token provider signs the plugin identity token JWT internally.+If a trust relationship exists between Vault and Azure through WIF, the secrets+engine can exchange the Vault identity token for a federated access token.++To configure a trusted relationship between Vault and Azure, :+- You must configure the [identity token issuer backend](/vault/api-docs/secret/identity/tokens#configure-the-identity-tokens-backend)+for Vault.+- Azure must have a+[federated identity credential](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-create-trust?pivots=identity-wif-apps-methods-azp#configure-a-federated-identity-credential-on-an-app)+configured with information about the fully qualified and network-reachable+issuer URL for the Vault plugin+[identity token provider](/vault/api-docs/secret/identity/tokens#read-plugin-identity-well-known-configurations).++Establishing a trusted relationship between Vault and Azure ensures that Azure+can fetch JWKS+[public keys](/vault/api-docs/secret/identity/tokens#read-active-public-keys)+and verify the plugin identity token signature.+1. Configure a role. A role may be set up with either an existing service principal, ora set of Azure roles that will be assigned to a dynamically created service principal.@@ -266,6 +302,56 @@ principles it creates.|------------------------------------------------| ------------ | ------------------------------------------- || [User Access Administrator][user_access_admin] | Subscription | Service Principal ID given in configuration |+## Plugin Workload Identity Federation (WIF)++<EnterpriseAlert product="vault" />++The Azure secrets engine supports the plugin WIF workflow, and has a source of identity called+a plugin identity token. The plugin identity token is a JWT that is signed internally by Vault's+[plugin identity token issuer](/vault/api-docs/secret/identity/tokens#read-plugin-workload-identity-issuer-s-openid-configuration).++If there is a trust relationship configured between Vault and Azure through+[workload identity federation](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation),+the secrets engine can exchange its identity token for short-lived access tokens needed to+perform its actions.++Exchanging identity tokens for access tokens lets the Azure secrets engine+operate without configuring explicit access to sensitive client credentials.++To configure the secrets engine to use plugin WIF:++1. Ensure that Vault [openid-configuration](/vault/api-docs/secret/identity/tokens#read-plugin-identity-token-issuer-s-openid-configuration)+and [public JWKS](/vault/api-docs/secret/identity/tokens#read-plugin-identity-token-issuer-s-public-jwks)+APIs are network-reachable by Azure. We recommend using an API proxy or gateway+if you need to limit Vault API exposure.++1. Configure a+[federated identity credential](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-create-trust?pivots=identity-wif-apps-methods-azp#configure-a-federated-identity-credential-on-an-app)+on a dedicated application registration in Azure to establish a trust relationship with Vault.+1. The issuer URL **must** point at your [Vault plugin identity token issuer](/vault/api-docs/secret/identity/tokens#read-plugin-workload-identity-issuer-s-openid-configuration) with the+`/.well-known/openid-configuration` suffix removed. For example:+`https://host:port/v1/identity/oidc/plugins`.+1. The subject identifier **must** match the unique `sub` claim issued by plugin identity tokens.+The subject identifier should have the form `plugin-identity:<NAMESPACE>:secret:<AZURE_MOUNT_ACCESSOR>`.+1. The audience should be under 600 characters. The default value in Azure is `api://AzureADTokenExchange`.++1. Configure the Azure secrets engine with the subscription, client and tenant IDs and the OIDC audience value.++```shell-session+$ vault write azure/config \+subscription_id=$AZURE_SUBSCRIPTION_ID \+tenant_id=$AZURE_TENANT_ID \+client_id=$AZURE_CLIENT_ID \+identity_token_audience="vault.example/v1/identity/oidc/plugins"+```++Your secrets engine can now use plugin WIF for its configuration credentials.+By default, WIF [credentials](https://learn.microsoft.com/en-us/entra/identity-platform/access-tokens#token-lifetime)+have a time-to-live of 1 hour and automatically refresh when they expire.++Please see the [API documentation](/vault/api-docs/secret/azure#configure-access)+for more details on the fields associated with plugin WIF.+## Choosing between dynamic or existing service principalsDynamic service principals are preferred if the desired Azure resources can be provided<82990506+hc-github-team-secure-vault-core@users.noreply.github.com>01ccf580d871b1c2af61ea7490690d4c400e131c (#27336)website/content/api-docs/auth/gcp.mdx | 33 +++++++-website/content/api-docs/secret/gcp.mdx | 39 ++++++++-website/content/docs/auth/gcp.mdx | 92 ++++++++++++++++++++-website/content/docs/secrets/gcp.mdx | 103 ++++++++++++++++++++++--4 files changed, 256 insertions(+), 11 deletions(-)
website/content/docs/internals/recommended-patterns.mdx+287 −0
@@ -0,0 +1,287 @@+---+layout: docs+page_title: Recommended patterns+description: Follow these recommended patterns to effectively operate Vault.+---++# Recommended patterns++Help keep your Vault environments operating effectively by implementing the following best practice so you avoid common anti-patterns.++| Description | Applicable Vault edition |+|--- |--- |+| [Adjust the default lease time](#adjust-the-default-lease-time) | All |+| [Use identity entities for accurate client count](#use-identity-entities-for-accurate-client-count) | Enterprise, HCP |+| [Increase IOPS](#increase-iops) | Enterprise, Community |+| [Enable disaster recovery](#enable-disaster-recovery) | Enterprise |+| [Test disaster recovery](#test-disaster-recovery) | Enterprise |+| [Improve upgrade cadence](#improve-upgrade-cadence) | Enterprise, Community |+| [Test before upgrades](#test-before-upgrades) | Enterprise, Community |+| [Rotate audit device logs](#rotate-audit-device-logs) | Enterprise, Community |+| [Monitor metrics](#monitor-metrics) | Enterprise, Community |+| [Establish usage baseline](#establish-usage-baseline) | Enterprise, Community |+| [Minimize root token use](#minimize-root-token-use) | All |+| [Rekey when necessary](#rekey-when-necessary) | All |++## Adjust the default lease time++The default lease time in Vault is 32 days or 768 hours. This time allows for some operations, such as re-authentication or renewal.+See [lease](/vault/docs/concepts/lease) documentation for more information.++**Recommended pattern:**++You should tune the lease TTL value for your needs. Vault holds leases in memory until the lease expires.+We recommend keeping TTLs as short as the use case will allow.+- [Auth tune](/vault/docs/commands/auth/tune)+- [Secrets tune](/vault/docs/commands/secrets/tune)++<Note>+Tuning or adjusting TTLs does not retroactively affect tokens that were issued. New tokens must be issued after tuning TTLs.+</Note>++**Anti-pattern issue:**++If you create leases without changing the default time-to-live (TTL), leases will live in Vault until the default lease time is up.+Depending on your infrastructure and available system memory, using the default or long TTL may cause performance issues as Vault stores+leases in memory.++## Use identity entities for accurate client count++Each Vault client may have multiple accounts with the auth methods enabled on the Vault server.++++**Recommended pattern:**++Since each token adds to the client count, and each unique authentication issues a token, you should use identity entities to create aliases that connect each login to a single identity.++- [Client count](/vault/docs/concepts/client-count)+- [Vault identity concepts](/vault/docs/concepts/identity)+- [Vault Identity secrets engine](/vault/docs/secrets/identity)+- [Identity: Entities and groups tutorial](/vault/tutorials/auth-methods/identity)++**Anti-pattern issue:**++When you do not use identity entities, each new client is counted as a separate identity when using another auth method not linked to the user's entity.++## Increase IOPS++IOPS (input/output operations per second) measures performance for Vault cluster members. Vault is bound by the IO limits of the storage backend rather than the compute requirements.++**Recommended pattern:**++Use the HashiCorp reference guidelines for Vault servers' hardware sizing and network considerations.++- [Vault with Integrated storage reference architecture](/vault/tutorials/day-one-raft/raft-reference-architecture#system-requirements)+- [Performance tuning](/vault/tutorials/operations/performance-tuning)+- [Transform secrets engine](/vault/docs/concepts/transform)++<Note>++Depending on the client count, the Transform (Enterprise) and Transit secret engines can be resource-intensive.++</Note>++**Anti-pattern issue:**++Limited IOPS can significantly degrade Vault’s performance.++## Enable disaster recovery++HashiCorp Vault's (HA) highly available [Integrated storage (Raft)](/vault/docs/concepts/integrated-storage)+backend provides intra-cluster data replication across cluster members. Integrated Storage provides Vault with+horizontal scalability and failure tolerance, but it does not provide backup for the entire cluster. Not utilizing+disaster recovery for your production environment will negatively impact your organization's Recovery Point+Objective (RPO) and Recovery Time Objective (RTO).++**Recommended pattern:**++For cluster-wide issues (i.e., network connectivity), Vault Enterprise Disaster Recovery (DR) replication+provides a warm standby cluster containing all primary cluster data. The DR cluster does not service reads+or writes but you can promote it to replace the primary cluster when needed.++- [Disaster recovery replication setup](/vault/tutorials/day-one-raft/disaster-recovery)+- [Disaster recovery (DR) replication](/vault/docs/enterprise/replication#disaster-recovery-dr-replication)+- [DR replication API documentation](/vault/api-docs/system/replication/replication-dr)++We also recommend periodically creating data snapshots to protect against data corruption.++- [Vault data backup standard procedure](/vault/tutorials/standard-procedures/sop-backup)+- [Automated integrated storage snapshots](/vault/docs/enterprise/automated-integrated-storage-snapshots)+- [/sys/storage/raft/snapshot-auto](/vault/api-docs/system/storage/raftautosnapshots)++**Anti-pattern issue:**++If you do not enable disaster recovery and catastrophic failure occurs, your use cases will encounter longer downtime duration and costs associated with not serving Vault clients in your environment.++## Test disaster recovery++Your disaster recovery (DR) solution is a key part of your overall disaster recovery plan.++Designing and configuring your Vault disaster recovery solution is only the first step. You also need to validate the DR solution, as not doing so can negatively impact your organization's Recovery Point Objective (RPO) and Recovery Time Objective (RTO).++**Recommended pattern:**++Vault's Disaster Recovery (DR) replication mode provides a warm standby for+failover if the primary cluster experiences catastrophic failure. You should+periodically test the disaster recovery replication cluster by completing the+failover and failback procedure.++- [Vault disaster recovery replication failover and failback tutorial](/vault/tutorials/enterprise/disaster-recovery-replication-failover)+- [Vault Enterprise replication](/vault/docs/enterprise/replication)+- [Monitoring Vault replication](/vault/tutorials/monitoring/monitor-replication)++You should establish standard operating procedures for restoring a Vault cluster from a snapshot. The restoration methods following a DR situation would be in response to data corruption or sabotage, which Disaster Recovery Replication might be unable to protect against.++- [Standard procedure for restoring a Vault cluster](/vault/tutorials/standard-procedures/sop-restore)++**Anti-pattern issue:**++If you don't test your disaster recovery solution, your key stakeholders will not feel confident they can effectively perform the disaster recovery plan. Testing the DR solution also helps your team to remove uncertainty around recovering the system during an outage.++## Improve upgrade cadence++While it might be easy to upgrade Vault whenever you have capacity, not having a frequent upgrade cadence can impact your Vault performance and security.++**Recommended pattern:**++We recommend upgrading to our latest version of Vault. Subscribe to the releases in [Vault's GitHub repository](https://github.com/hashicorp/vault), and notifications from [HashiCorp Vault discuss](https://discuss.hashicorp.com/c/release-notifications/57), will inform you when we release a new Vault version.++- [Vault upgrade guides](/vault/docs/upgrading)+- [Vault feature deprecation notice and plans](/vault/docs/deprecation)++**Anti-pattern issue:**++When you do not keep a regular upgrade cadence, your Vault environment could be missing key features or improvements.++- Missing patches for bugs or vulnerabilities as documented in the [CHANGELOG](https://github.com/hashicorp/vault/blob/main/CHANGELOG.md).+- New features to improve workflow.+- Must use version-specific rather than the latest documentation.+- Some educational resourcesrequire a specific minimum Vault version.+- Updates may require a stepped approach that uses an intermediate version before installing the latest binary.++## Test before upgrades++We recommend testing Vault in a sandbox environment before deploying to production.++Although it might be faster to upgrade immediately in production, testing will help identify any compatibility issues.++Be aware of the [CHANGELOG](https://github.com/hashicorp/vault/blob/main/CHANGELOG.md) and account for any new features, improvements, known issues and bug fixes in your testing.++**Recommended pattern:**++Test new Vault versions in sandbox environments before upgrading in production and follow our upgrading documentation.++We recommend adding a testing phase to your standard upgrade procedure.++- [Vault upgrade standard procedure](/vault/tutorials/standard-procedures/sop-upgrade)+- [Upgrading Vault](/vault/docs/upgrading)++**Anti-pattern issue:**++Without adequate testing before upgrading in production, you risk compatibility and performance issues.++<Warning>++This could lead to downtime or degradation in your production Vault environment.++</Warning>++## Rotate audit device logs++Audit devices in Vault maintain a detailed log of every client request and server response.++If you allow the logs for audit devices to run perpetually without rotating you may face a blocked audit device if the filesystem storage becomes exhausted.++**Recommended pattern:**++Inspect and rotate audit logs periodically.++- [Blocked audit devices tutorial](/vault/tutorials/monitoring/blocked-audit-devices)+- [blocked audit devices](/vault/docs/audit#blocked-audit-devices)++**Anti-pattern issue:**++Vault will not respond to requests when audit devices are not enabled to record them.++The audit device can exhaust the local storage if the audit device log is not maintained and rotated over time.++## Monitor metrics++Relying solely on Vault operational logs and data in Vault UI will give you a partial picture of the cluster's performance.+++**Recommended pattern:**++Continuous monitoring will allow organizations to detect minor problems and promptly resolve them.+Migrating from reactive to proactive monitoring will help to prevent system failures. Vault has multiple outputs+that help monitor the cluster's activity: audit logs, operational logs, and telemetry data. This data can work+with a SIEM (security information and event management) tool for aggregation, inspection, and alerting capabilities.++- [Telemetry](/vault/docs/internals/telemetry#secrets-engines-metric)+- [Telemetry metrics reference](/vault/tutorials/monitoring/telemetry-metrics-reference)++Adding a monitoring solution:+- [Audit device logs and incident response with elasticsearch](/vault/tutorials/monitoring/audit-elastic-incident-response)+- [Monitor telemetry & audit device log data](/vault/tutorials/monitoring/monitor-telemetry-audit-splunk)+- [Monitor telemetry with Prometheus & Grafana](/vault/tutorials/monitoring/monitor-telemetry-grafana-prometheus)+++<Note>++Vault logs to standard output and standard error by default, automatically captured by the systemd journal. You can also instruct Vault to redirect operational log writes to a file.++</Note>++**Anti-pattern issue:**++Having partial insight into cluster activity can leave the business in a reactive state.++## Establish usage baseline++A baseline provides insight into current utilization and thresholds. Telemetry metrics are valuable, especially when monitored over time. You can use telemetry metrics to gather a baseline of cluster activity, while alerts inform you of abnormal activity.++**Recommended pattern:**++Telemetry information can also be streamed directly from Vault to a range of metrics aggregation solutions and+saved for aggregation and inspection.++- [Vault usage metrics](/vault/tutorials/monitoring/usage-metrics)+- [Diagnose server issues](/vault/tutorials/monitoring/diagnose-startup-issues)++**Anti-pattern issue:**++This issue closely relates to the recommended pattern for [monitor metrics](#monitor-metrics).+Telemetry data is+only held in memory for a short period.++## Minimize root token use++Initializing a Vault server emits an initial root token that gives root-level access across all Vault features.++**Recommended pattern:**++We recommend that you revoke the root token after initializing Vault within your environment. If users require elevated access, create access control list policies that grant proper capabilities on the necessary paths in Vault. If your operations require the root token, keep it for the shortest possible time before revoking it.++- [Generate root tokens tutorial](/vault/tutorials/operations/generate-root)+- [Root tokens](/vault/docs/concepts/tokens#root-tokens)+- [Vault policies](/vault/docs/concepts/policies)++**Anti-pattern issue:**++A root token can perform all actions within Vault and never expire. Unrestricted access can give users higher privileges than necessary to all Vault operations and paths. Sharing and providing access to root tokens poses a security risk.++## Rekey when necessary++Vault distributes unsealed keys to stakeholders. A quorum of keys is needed to unlock Vault based on your initialization settings.++**Recommended pattern:**++Vault supports rekeying, and you should establish a workflow for rekeying when necessary.++- [Rekeying & rotating Vault](/vault/tutorials/operations/rekeying-and-rotating)+- [Operator rekey](/vault/docs/commands/operator/rekey)++**Anti-pattern issue:**++If several stakeholders leave the organization, you risk not having the required key shares to meet the unseal quorum, which could result in the loss of the ability to unseal Vault.
ui/tests/helpers/clients/client-count-helpers.js+372 −4
@@ -40,7 +40,7 @@ export function assertBarChart(assert, chartName, byMonthData, isStacked = false}export const ACTIVITY_RESPONSE_STUB = {-start_time: '2023-08-01T00:00:00Z',+start_time: '2023-06-01T00:00:00Z',end_time: '2023-09-30T23:59:59Z', // is always the last day and hour of the month queriedby_namespace: [{@@ -148,11 +148,209 @@ export const ACTIVITY_RESPONSE_STUB = {],months: [{-timestamp: '2023-08-01T00:00:00Z',+timestamp: '2023-06-01T00:00:00Z',counts: null,namespaces: null,new_clients: null,},+{+timestamp: '2023-07-01T00:00:00Z',+counts: {+acme_clients: 100,+clients: 100,+entity_clients: 100,+non_entity_clients: 100,+secret_syncs: 100,+distinct_entities: 100,+non_entity_tokens: 100,+},+namespaces: [+{+namespace_id: 'root',+namespace_path: '',+counts: {+acme_clients: 100,+clients: 100,+entity_clients: 100,+non_entity_clients: 100,+secret_syncs: 100,+distinct_entities: 100,+non_entity_tokens: 100,+},+mounts: [+{+mount_path: 'pki-engine-0',+counts: {+acme_clients: 100,+clients: 100,+entity_clients: 0,+non_entity_clients: 0,+secret_syncs: 0,+distinct_entities: 0,+non_entity_tokens: 0,+},+},+{+mount_path: 'auth/authid/0',+counts: {+acme_clients: 0,+clients: 100,+entity_clients: 100,+non_entity_clients: 100,+secret_syncs: 0,+distinct_entities: 0,+non_entity_tokens: 0,+},+},+{+mount_path: 'kvv2-engine-0',+counts: {+acme_clients: 0,+clients: 100,+entity_clients: 0,+non_entity_clients: 0,+secret_syncs: 100,+distinct_entities: 0,+non_entity_tokens: 0,+},+},+],+},+],+new_clients: {+counts: {+acme_clients: 100,+clients: 100,+entity_clients: 100,+non_entity_clients: 100,+secret_syncs: 100,+distinct_entities: 100,+non_entity_tokens: 100,+},+namespaces: [+{+namespace_id: 'root',+namespace_path: '',+counts: {+acme_clients: 100,+clients: 100,+entity_clients: 100,+non_entity_clients: 100,+secret_syncs: 100,+distinct_entities: 100,+non_entity_tokens: 100,+},+mounts: [+{+mount_path: 'pki-engine-0',+counts: {+acme_clients: 100,+clients: 100,+entity_clients: 0,+non_entity_clients: 0,+secret_syncs: 0,+distinct_entities: 0,+non_entity_tokens: 0,+},+},+{+mount_path: 'auth/authid/0',+counts: {+acme_clients: 0,+clients: 100,+entity_clients: 100,+non_entity_clients: 100,+secret_syncs: 0,+distinct_entities: 0,+non_entity_tokens: 0,+},+},+{+mount_path: 'kvv2-engine-0',+counts: {+acme_clients: 0,+clients: 100,+entity_clients: 0,+non_entity_clients: 0,+secret_syncs: 100,+distinct_entities: 0,+non_entity_tokens: 0,+},+},+],+},+],+},+},+{+timestamp: '2023-08-01T00:00:00Z',+counts: {+acme_clients: 100,+clients: 100,+entity_clients: 100,+non_entity_clients: 100,+secret_syncs: 100,+distinct_entities: 100,+non_entity_tokens: 100,+},+namespaces: [+{+namespace_id: 'root',+namespace_path: '',+counts: {+acme_clients: 100,+clients: 100,+entity_clients: 100,+non_entity_clients: 100,+secret_syncs: 100,+distinct_entities: 100,+non_entity_tokens: 100,+},+mounts: [+{+mount_path: 'pki-engine-0',+counts: {+acme_clients: 100,+clients: 100,+entity_clients: 0,+non_entity_clients: 0,+secret_syncs: 0,+distinct_entities: 0,+non_entity_tokens: 0,+},+},+{+mount_path: 'auth/authid/0',+counts: {+acme_clients: 0,+clients: 100,+entity_clients: 100,+non_entity_clients: 100,+secret_syncs: 0,+distinct_entities: 0,+non_entity_tokens: 0,+},+},+{+mount_path: 'kvv2-engine-0',+counts: {+acme_clients: 0,+clients: 100,+entity_clients: 0,+non_entity_clients: 0,+secret_syncs: 100,+distinct_entities: 0,+non_entity_tokens: 0,+},+},+],+},+],+new_clients: {+counts: null,+namespaces: null,+},+},{timestamp: '2023-09-01T00:00:00Z',counts: {@@ -646,10 +844,323 @@ export const SERIALIZED_ACTIVITY_RESPONSE = {],by_month: [{-month: '8/23',-timestamp: '2023-08-01T00:00:00Z',+month: '6/23',+timestamp: '2023-06-01T00:00:00Z',namespaces: [],namespaces_by_key: {},+new_clients: {+month: '6/23',+timestamp: '2023-06-01T00:00:00Z',+namespaces: [],+},+},+{+month: '7/23',+timestamp: '2023-07-01T00:00:00Z',+acme_clients: 100,+clients: 100,+entity_clients: 100,+non_entity_clients: 100,+secret_syncs: 100,+namespaces: [+{+label: 'root',+acme_clients: 100,+clients: 100,+entity_clients: 100,+non_entity_clients: 100,+secret_syncs: 100,+mounts: [+{+label: 'pki-engine-0',+acme_clients: 100,+clients: 100,+entity_clients: 0,+non_entity_clients: 0,+secret_syncs: 0,+},+{+label: 'auth/authid/0',+acme_clients: 0,+clients: 100,+entity_clients: 100,+non_entity_clients: 100,+secret_syncs: 0,+},+{+label: 'kvv2-engine-0',+acme_clients: 0,+clients: 100,+entity_clients: 0,+non_entity_clients: 0,+secret_syncs: 100,+},+],+},+],+namespaces_by_key: {+root: {+acme_clients: 100,+clients: 100,+entity_clients: 100,+non_entity_clients: 100,+secret_syncs: 100,+timestamp: '2023-07-01T00:00:00Z',+month: '7/23',+new_clients: {+month: '7/23',+timestamp: '2023-07-01T00:00:00Z',+label: 'root',+acme_clients: 100,+clients: 100,+entity_clients: 100,+non_entity_clients: 100,+secret_syncs: 100,+mounts: [+{+label: 'pki-engine-0',+acme_clients: 100,+clients: 100,+entity_clients: 0,+non_entity_clients: 0,+secret_syncs: 0,+},+{+label: 'auth/authid/0',+acme_clients: 0,+clients: 100,+entity_clients: 100,+non_entity_clients: 100,+secret_syncs: 0,+},+{+label: 'kvv2-engine-0',+acme_clients: 0,+clients: 100,+entity_clients: 0,+non_entity_clients: 0,+secret_syncs: 100,+},+],+},+mounts_by_key: {+'pki-engine-0': {+label: 'pki-engine-0',+acme_clients: 100,+clients: 100,+entity_clients: 0,+non_entity_clients: 0,+secret_syncs: 0,+timestamp: '2023-07-01T00:00:00Z',+month: '7/23',+new_clients: {+month: '7/23',+timestamp: '2023-07-01T00:00:00Z',+label: 'pki-engine-0',+acme_clients: 100,+clients: 100,+entity_clients: 0,+non_entity_clients: 0,+secret_syncs: 0,+},+},+'auth/authid/0': {+label: 'auth/authid/0',+acme_clients: 0,+clients: 100,+entity_clients: 100,+non_entity_clients: 100,+secret_syncs: 0,+timestamp: '2023-07-01T00:00:00Z',+month: '7/23',+new_clients: {+month: '7/23',+timestamp: '2023-07-01T00:00:00Z',+label: 'auth/authid/0',+acme_clients: 0,+clients: 100,+entity_clients: 100,+non_entity_clients: 100,+secret_syncs: 0,+},+},+'kvv2-engine-0': {+label: 'kvv2-engine-0',+acme_clients: 0,+clients: 100,+entity_clients: 0,+non_entity_clients: 0,+secret_syncs: 100,+timestamp: '2023-07-01T00:00:00Z',+month: '7/23',+new_clients: {+month: '7/23',+timestamp: '2023-07-01T00:00:00Z',+label: 'kvv2-engine-0',+acme_clients: 0,+clients: 100,+entity_clients: 0,+non_entity_clients: 0,+secret_syncs: 100,+},+},+},+},+},+new_clients: {+month: '7/23',+timestamp: '2023-07-01T00:00:00Z',+acme_clients: 100,+clients: 100,+entity_clients: 100,+non_entity_clients: 100,+secret_syncs: 100,+namespaces: [+{+label: 'root',… diff truncated
website/content/docs/platform/k8s/vso/helm.mdx+72 −5
@@ -11,7 +11,7 @@ The chart is customizable using[Helm configuration values](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing).<!-- DO NOT EDIT. The docs below are generated automatically. To change, edit-the vault-secrets-operator repo's values.yaml: file commit=787f43ac8f6e9a8c57e9a5d1f915fe2ec04abd6c -->+the vault-secrets-operator repo's values.yaml: file commit=f9ddeb63c4d884360c3eeb127d09d13de34393f4 --><!-- codegen: start -->## Top-Level Stanzas@@ -34,6 +34,16 @@ Use these links to navigate to a particular top-level stanza.- `replicas` ((#v-controller-replicas)) (`integer: 1`) - Set the number of replicas for the operator.+- `strategy` ((#v-controller-strategy)) (`object: ""`) - Configure update strategy for multi-replica deployments.+Kubernetes supports types Recreate, and RollingUpdate+ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy+Example:+strategy: {}+rollingUpdate:+maxSurge: 1+maxUnavailable: 0+type: RollingUpdate+- `hostAliases` ((#v-controller-hostaliases)) (`array<map>`) - Host Aliases settings for vault-secrets-operator pod.The value is an array of PodSpec HostAlias maps.ref: https://kubernetes.io/docs/tasks/network/customize-hosts-file-for-pods/@@ -74,6 +84,38 @@ Use these links to navigate to a particular top-level stanza.- antarctica-east1- antarctica-west1+- `rbac` ((#v-controller-rbac))++- `clusterRoleAggregation` ((#v-controller-rbac-clusterroleaggregation)) - clusterRoleAggregation defines the roles included in the aggregated ClusterRole.++- `viewerRoles` ((#v-controller-rbac-clusterroleaggregation-viewerroles)) (`array<string>: []`) - viewerRoles is a list of roles that will be aggregated into the viewer ClusterRole.+The role name must be that of any VSO resource type. E.g. "VaultAuth", "HCPAuth".+All values are case-insensitive.+Specifying '*' as the first element will include all roles in the aggregation.++The ClusterRole name takes the form of `<chart-fullname>`-aggregate-role-viewer.++Example usages:+all roles:+- '*'+individually specified roles:+- "VaultAuth"+- "HCPAuth"++- `editorRoles` ((#v-controller-rbac-clusterroleaggregation-editorroles)) (`array<string>: []`) - editorRoles is a list of roles that will be aggregated into the editor ClusterRole.+The role name must be that of any VSO resource type. E.g. "VaultAuth", "HCPAuth".+All values are case-insensitive.+Specifying '*' as the first element will include all roles in the aggregation.++The ClusterRole name takes the form of `<chart-fullname>`-aggregate-role-editor.++Example usages:+all roles:+- '*'+individually specified roles:+- "VaultAuth"+- "HCPAuth"+- `kubeRbacProxy` ((#v-controller-kuberbacproxy)) - Settings related to the kubeRbacProxy container. This container is an HTTP proxy for thecontroller manager which performs RBAC authorization against the Kubernetes API using SubjectAccessReviews.@@ -123,7 +165,21 @@ Use these links to navigate to a particular top-level stanza.- `repository` ((#v-controller-manager-image-repository)) (`string: hashicorp/vault-secrets-operator`)-- `tag` ((#v-controller-manager-image-tag)) (`string: 0.6.0`)+- `tag` ((#v-controller-manager-image-tag)) (`string: 0.7.0`)++- `logging` ((#v-controller-manager-logging)) - logging++- `level` ((#v-controller-manager-logging-level)) (`string: info`) - Sets the log level for the operator.+Builtin levels are: info, error, debug, debug-extended, trace+Default: info++- `timeEncoding` ((#v-controller-manager-logging-timeencoding)) (`string: rfc3339`) - Sets the time encoding for the operator.+Options are: epoch, millis, nano, iso8601, rfc3339, rfc3339nano+Default: rfc3339++- `stacktraceLevel` ((#v-controller-manager-logging-stacktracelevel)) (`string: panic`) - Sets the stacktrace level for the operator.+Options are: info, error, panic+Default: panic- `globalTransformationOptions` ((#v-controller-manager-globaltransformationoptions)) - Global secret transformation options. In addition to the boolean optionsbelow, these options may be set via the@@ -133,6 +189,19 @@ Use these links to navigate to a particular top-level stanza.- `excludeRaw` ((#v-controller-manager-globaltransformationoptions-excluderaw)) (`boolean: false`) - excludeRaw directs the operator to prevent _raw secret data being storedin the destination K8s Secret.+- `backoffOnSecretSourceError` ((#v-controller-manager-backoffonsecretsourceerror)) - Backoff settings for the controller manager. These settings control the backoff behavior+when the controller encounters an error while fetching secrets from the SecretSource.++- `initialInterval` ((#v-controller-manager-backoffonsecretsourceerror-initialinterval)) (`duration: 5s`) - Initial interval between retries.++- `maxInterval` ((#v-controller-manager-backoffonsecretsourceerror-maxinterval)) (`duration: 60s`) - Maximum interval between retries.++- `maxElapsedTime` ((#v-controller-manager-backoffonsecretsourceerror-maxelapsedtime)) (`duration: 0s`) - Maximum elapsed time before giving up.++- `randomizationFactor` ((#v-controller-manager-backoffonsecretsourceerror-randomizationfactor)) (`float: 0.5`) - Randomization factor to add jitter to the interval between retries.++- `multiplier` ((#v-controller-manager-backoffonsecretsourceerror-multiplier)) (`float: 1.5`) - Sets the multiplier for increasing the interval between retries.+- `clientCache` ((#v-controller-manager-clientcache)) - Configures the client cache which is used by the controller to cache (and potentially persist) vault tokens thatare the result of using the VaultAuthMethod. This enables re-use of Vault Tokensthroughout their TTLs as well as the ability to renew.@@ -301,8 +370,6 @@ Use these links to navigate to a particular top-level stanza.- `extraArgs` ((#v-controller-manager-extraargs)) (`array: []`) - Defines additional commandline arguments to be passed to thevault-secrets-operator manager container.-extraArgs:-- -zap-log-level=5- `resources` ((#v-controller-manager-resources)) (`map`) - Configures the default resources for the vault-secrets-operator container.For more information on configuring resources, see the K8s documentation:@@ -520,7 +587,7 @@ Use these links to navigate to a particular top-level stanza.- `serviceMonitor` ((#v-telemetry-servicemonitor))-- `enabled` ((#v-telemetry-servicemonitor-enabled)) (`boolean: false`) - The Prometheus operator *must* be installed before enabling this feature,+- `nabled` ((#v-telemetry-servicemonitor-nabled)) (`boolean: false`) - The Prometheus operator *must* be installed before enabling this feature,if not the chart will fail to install due to missing CustomResourceDefinitionsprovided by the operator.
website/content/api-docs/auth/gcp.mdx+31 −2
@@ -21,7 +21,25 @@ at any location, please update your API calls accordingly.Configures the credentials required for the plugin to perform API callsto Google Cloud. These credentials will be used to query the status of IAMentities and get service account or other Google public certificates-to confirm signed JWTs passed in during login.+to confirm signed JWTs passed in during login. You can configure+credentials either with Application Credentials for a privileged service account,+or using Plugin Workload Identity Federation (WIF).++### IAM+Vault uses the official Google Cloud SDK to source credentials from environment variables and shared files.++From the highest precedence to lowest, you can pass root credentials to the Vault server in the following ways:++1. Provide static credentials to the API as a payload.++1. Use [plugin workload identity federation](/vault/docs/auth/gcp#plugin-workload-identity-federation-wif) credentials.++1. Set [application default credentials](https://cloud.google.com/docs/authentication/application-default-credentials)+as environment variables on the Vault server.++<Warning title="Destructive action">+Passing Vault new root credentials overwrites any preexisting root credentials.+</Warning>| Method | Path || :----- | :----------------- |@@ -33,7 +51,18 @@ to confirm signed JWTs passed in during login.service account credentials file. The service account associated with the credentialsfile must have the following [permissions](/vault/docs/auth/gcp#required-gcp-permissions).If this value is empty, Vault will try to use [Application Default Credentials][gcp-adc]-from the machine on which the Vault server is running.+from the machine on which the Vault server is running. Mutually exclusive with `identity_token_audience`.++- `service_account_email` `(string: "")` – <EnterpriseAlert product="vault" inline /> Service Account+to impersonate for plugin workload identity federation. Required with `identity_token_audience`.++- `identity_token_audience` `(string: "")` - <EnterpriseAlert product="vault" inline /> The+audience claim value for plugin identity tokens. Must match an allowed audience configured+for the target [Workload Identity Pool](https://cloud.google.com/iam/docs/workload-identity-federation-with-other-providers#prepare).+Mutually exclusive with `credentials`.++- `identity_token_ttl` `(string/int: 3600)` - <EnterpriseAlert product="vault" inline /> The+TTL of generated tokens. Defaults to 1 hour. Uses [duration format strings](/vault/docs/concepts/duration-format).- `iam_alias` `(string: "role_id")` - Must be either `unique_id` or `role_id`.If `unique_id` is specified, the service account's unique ID will be used for
website/content/api-docs/secret/gcp.mdx+37 −2
@@ -16,18 +16,53 @@ update your API calls accordingly.## Write config+Use the endpoint to configure shared information for the secrets engine. You can configure+credentials for a privileged service account either with Application Credentials or using+Plugin Workload Identity Federation (WIF).++### IAM+Vault uses the official Google Cloud SDK to source credentials from environment+variables and shared files.++From the highest precedence to lowest, you can pass root credentials to the Vault+server in the following ways:++1. Provide static credentials to the API as a payload.++1. Use [plugin workload identity federation](/vault/docs/secrets/gcp#plugin-workload-identity-federation-wif)+credentials.++1. Set [application default credentials](https://cloud.google.com/docs/authentication/application-default-credentials)+as environment variables on the Vault server.++1. Define credentials in shared credential files.++<Warning title="Destructive action">+Passing Vault new root credential overwrites any preexisting root credentials.+</Warning>+| Method | Path || :----- | :------------ || `POST` | `/gcp/config` |-This endpoint configures shared information for the secrets engine.### Parameters- `credentials` (`string:""`) - JSON credentials (either file contents or '@path/to/file')See docs for [alternative ways](/vault/docs/secrets/gcp#setup)to pass in to this parameter, as well as the-[required permissions](/vault/docs/secrets/gcp#required-permissions).+[required permissions](/vault/docs/secrets/gcp#required-permissions). Mutually exclusive with `identity_token_audience`.++- `service_account_email` `(string: "")` – <EnterpriseAlert product="vault" inline /> Service Account+to impersonate for plugin workload identity federation. Required with `identity_token_audience`.++- `identity_token_audience` `(string: "")` - <EnterpriseAlert product="vault" inline /> The+audience claim value for plugin identity tokens. Must match an allowed audience configured+for the target [Workload Identity Pool](https://cloud.google.com/iam/docs/workload-identity-federation-with-other-providers#prepare).+Mutually exclusive with `credentials`.++- `identity_token_ttl` `(string/int: 3600)` - <EnterpriseAlert product="vault" inline /> The+TTL of generated tokens. Defaults to 1 hour. Uses [duration format strings](/vault/docs/concepts/duration-format).- `ttl` (`int: 0 || string:"0s"`) – Specifies default config TTL for long-lived credentials(i.e. service account keys). Uses [duration format strings](/vault/docs/concepts/duration-format).
website/content/api-docs/secret/pki.mdx+57 −13
@@ -672,10 +672,11 @@ It is suggested to limit access to the path-overridden issue endpoint (onsigned certificate. This field is validated against `allowed_user_ids` onthe role.-- `metadata` `(string: "")` - <EnterpriseAlert inline="true" /> A blank-or base 64 encoded value to be associated with the certificate's serial-number. The role's `no_store_metadata` must be set to false, otherwise an-error is returned when specified.+- `cert_metadata` `(string: "")` - <EnterpriseAlert inline="true" /> A base 64+encoded value or an empty string to associate with the certificate's serial+number. The role's no_store_metadata must be set to false, otherwise an+error is returned when specified. To retrieve metadata see:+[Read Certificate Metadata](#read-certificate-metadata)#### Sample payload@@ -903,10 +904,11 @@ It is suggested to limit access to the path-overridden sign endpoint (onsigned certificate. This field is validated against `allowed_user_ids` onthe role.-- `metadata` `(string: "")` - <EnterpriseAlert inline="true" /> A blank-or base 64 encoded value to be associated with the certificate's serial-number. The role's `no_store_metadata` must be set to false, otherwise an-error is returned when specified.+- `cert_metadata` `(string: "")` - <EnterpriseAlert inline="true" /> A base 64+encoded value or an empty string to associate with the certificate's serial+number. The role's no_store_metadata must be set to false, otherwise an+error is returned when specified. To retrieve metadata see:+[Read Certificate Metadata](#read-certificate-metadata)#### Sample payload@@ -1474,10 +1476,12 @@ have access.**User ID (OID 0.9.2342.19200300.100.1.1) Subject values to be placed on thesigned certificate. No validation on names is performed using this endpoint.-- `metadata` `(string: "")` - <EnterpriseAlert inline="true" /> A blank-or base 64 encoded value to be associated with the certificate's serial-number. The role's `no_store_metadata` must be set to false, otherwise an-error is returned when specified.+- `cert_metadata` `(string: "")` - <EnterpriseAlert inline="true" /> A base 64+encoded value or an empty string to associate with the certificate's serial+number. A role must be passed to sign-verbatim, and that role's+no_store_metadata must be set to false, otherwise an error is returned when+specified. To retrieve metadata see:+[Read Certificate Metadata](#read-certificate-metadata)#### Sample payload@@ -2176,13 +2180,53 @@ $ curl \"data": {"issuer_id": "e27bf456-51e1-d937-0001-4a609184fd9b","expiration": "2022-11-02T14:41:47.327515Z",-"metadata": "user-provided-metadata",+"cert_metadata": "dXNlci1wcm92aWRlZC1tZXRhZGF0YQ==","role": "role-name","serial_number": "67:b4:f7:2c:aa:ef:b9:30:f6:ae:f5:12:21:79:ac:08:8a:86:89:72"}}```+#### Sample cert_metadata fetch++```shell-session+$ base64 --decode <<< $(vault read --field=cert_metadata pki/cert-metadata/67:b4:f7:2c:aa:ef:b9:30:f6:ae:f5:12:21:79:ac:08:8a:86:89:72 )+user-provided-metadata+```++### List Certificate Metadata <EnterpriseAlert inline="true" />++This endpoint returns a list of stored certificate metadata. Only the+serial numbers of the certificates the metadata is associated with are+returned, not the certificate metadata itself.++| Method | Path |+| :----- | :------------------- |+| `LIST` | `/pki/cert-metadata` |++#### Sample request++```shell-session+$ curl \+--header "X-Vault-Token: ..." \+--request LIST \+http://127.0.0.1:8200/v1/pki/cert-metadata+```++#### Sample response++```json+{+"auth": null,+"data": {+"keys": ["38:1f:29:ad:99:e8:c9:ae:7b:33:4d:b2:a5:c8:30:7c:71:93:77:ee", "67:b4:f7:2c:aa:ef:b9:30:f6:ae:f5:12:21:79:ac:08:8a:86:89:72"]+},+"lease_duration": 0,+"lease_id": "",+"renewable": false+}+```+---## Managing keys and issuers<82990506+hc-github-team-secure-vault-core@users.noreply.github.com>when off (#27376)changelog/27366.txt | 3 ++ui/app/adapters/aws-credential.js | 2 +-ui/app/models/aws-credential.js | 3 +-.../components/generate-credentials.hbs | 1 +ui/lib/core/addon/components/form-field.js | 8 ++-ui/tests/acceptance/aws-test.js | 50 ++++++++++++-------.../integration/components/form-field-test.js | 29 ++++++++++-ui/tests/unit/adapters/aws-credential-test.js | 5 ++8 files changed, 78 insertions(+), 23 deletions(-)create mode 100644 changelog/27366.txt
website/content/api-docs/auth/azure.mdx+11 −1
@@ -22,6 +22,9 @@ Configures the credentials required for the plugin to perform API callsto Azure. These credentials will be used to query the metadata about thevirtual machine.+You can configure the auth engine with account credentials or plugin workload+identity federation (WIF).+| Method | Path || :----- | :------------------- || `POST` | `/auth/azure/config` |@@ -41,7 +44,14 @@ virtual machine.This value can also be provided with the `AZURE_CLIENT_ID` environment variable.- `client_secret` `(string: '')` - The client secret for credentials to query the Azure APIs.This value can also be provided with the `AZURE_CLIENT_SECRET` environment variable.-- `max_retries` `(int: 3)` - The maximum number of attempts a failed operation will be+Mutually exclusive with `identity_token_audience`.+- `identity_token_audience` `(string: "")` - <EnterpriseAlert product="vault" inline /> The+audience claim value for plugin identity tokens. Must match the allowed audiences configured+for the target [Federated Identity Credential](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-create-trust?pivots=identity-wif-apps-methods-azp#configure-a-federated-identity-credential-on-an-app).+Mutually exclusive with `client_secret`.+- `identity_token_ttl` `(string/int: 3600)` - <EnterpriseAlert product="vault" inline /> The+TTL of generated tokens. Defaults to 1 hour. Uses [duration format strings](/vault/docs/concepts/duration-format).+- `max_retries` `(int: 3)` - The maximum number of attempts a failed operation will beretried before producing an error.- `max_retry_delay` `(string: '60s')` - The maximum delay, in seconds, allowed before retrying an operation.- `retry_delay` `(string: '4s')` - The initial amount of delay, in seconds, to use before retrying an operation. Increases exponentially.
website/content/api-docs/secret/azure.mdx+10 −0
@@ -20,6 +20,9 @@ Configures the credentials required for the plugin to perform API callsto Azure. These credentials will be used to query roles and create/deleteservice principals. Environment variables will override any parameters set in the config.+You can configure the secrets engine with account credentials or using+plugin workload identity federation (WIF).+| Method | Path || :----- | :-------------- || `POST` | `/azure/config` |@@ -32,6 +35,13 @@ service principals. Environment variables will override any parameters set in thwith the AZURE_CLIENT_ID environment variable. See [authentication](/vault/docs/secrets/azure#authentication) for more details.- `client_secret` (`string:""`) - The OAuth2 client secret to connect to Azure. This value can also beprovided with the AZURE_CLIENT_SECRET environment variable. See [authentication](/vault/docs/secrets/azure#authentication) for more details.+Mutually exclusive with `identity_token_audience`.+- `identity_token_audience` `(string: "")` - <EnterpriseAlert product="vault" inline /> The+audience claim value for plugin identity tokens. Must match the allowed audiences configured+for the target [Federated Identity Credential](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-create-trust?pivots=identity-wif-apps-methods-azp#configure-a-federated-identity-credential-on-an-app).+Mutually exclusive with `client_secret`.+- `identity_token_ttl` `(string/int: 3600)` - <EnterpriseAlert product="vault" inline /> The+TTL of generated tokens. Defaults to 1 hour. Uses [duration format strings](/vault/docs/concepts/duration-format).- `environment` (`string:""`) - The Azure environment. This value can also be provided with the AZURE_ENVIRONMENTenvironment variable. If not specified, Vault will use Azure Public Cloud.- `root_password_ttl` `(string: 182d)` - Specifies how long the root password is valid for in Azure when
ui/tests/integration/components/tools/tool-wrap-test.js+81 −0
@@ -0,0 +1,81 @@+/**+* Copyright (c) HashiCorp, Inc.+* SPDX-License-Identifier: BUSL-1.1+*/++import { module, test } from 'qunit';+import { setupRenderingTest } from 'vault/tests/helpers';+import { setupMirage } from 'ember-cli-mirage/test-support';+import { click, fillIn, render } from '@ember/test-helpers';+import { hbs } from 'ember-cli-htmlbars';+import sinon from 'sinon';+import { GENERAL } from 'vault/tests/helpers/general-selectors';+import codemirror from 'vault/tests/helpers/codemirror';+import { TOOLS_SELECTORS as TS } from 'vault/tests/helpers/tools-selectors';++module('Integration | Component | tools/tool-wrap', function (hooks) {+setupRenderingTest(hooks);+setupMirage(hooks);++hooks.beforeEach(function () {+this.onBack = sinon.spy();+this.onClear = sinon.spy();+this.onChange = sinon.spy();+this.data = '{\n}';+this.renderComponent = async () => {+await render(hbs`+<ToolWrap+@token={{this.token}}+@errors={{this.errors}}+@onClear={{this.onClear}}+@onBack={{this.onBack}}+@onChange={{this.onChange}}+@data={{this.data}}+/>`);+};+});++test('it renders defaults', async function (assert) {+await this.renderComponent();++assert.dom('h1').hasText('Wrap Data', 'Title renders');+assert.strictEqual(codemirror().getValue(' '), '{ }', 'json editor initializes with empty object');+assert.dom(GENERAL.toggleInput('Wrap TTL')).isNotChecked('Wrap TTL defaults to unchecked');+assert.dom(TS.submit).isEnabled();+assert.dom(TS.toolsInput('wrapping-token')).doesNotExist();+assert.dom(TS.button('Back')).doesNotExist();+assert.dom(TS.button('Done')).doesNotExist();+});++test('it renders token view', async function (assert) {+this.token = 'blah.jhfel7SmsVeZwihaGiIKHGh2cy5XZWtEeEt5WmRwS1VYSTNDb1BBVUNsVFAQ3JIK';+await this.renderComponent();++assert.dom('h1').hasText('Wrap Data');+assert.dom('label').hasText('Wrapped token');+assert.dom('.CodeMirror').doesNotExist();+assert.dom(TS.toolsInput('wrapping-token')).hasText(this.token);+await click(TS.button('Back'));+assert.true(this.onBack.calledOnce, 'onBack is called');+await click(TS.button('Done'));+assert.true(this.onClear.calledOnce, 'onClear is called');+});++test('it calls onChange for json editor', async function (assert) {+const data = `{"foo": "bar"}`;+await this.renderComponent();+await codemirror().setValue(`{bad json}`);+assert.dom(TS.submit).isDisabled('submit disables if json editor has linting errors');++await codemirror().setValue(data);+assert.dom(TS.submit).isEnabled('submit reenables if json editor has no linting errors');+assert.propEqual(this.onChange.lastCall.args, ['data', data], 'onChange is called with json data');+});++test('it calls onChange for ttl picker', async function (assert) {+await this.renderComponent();+await click(GENERAL.toggleInput('Wrap TTL'));+await fillIn(GENERAL.ttl.input('Wrap TTL'), '20');+assert.propEqual(this.onChange.lastCall.args, ['wrapTTL', '1200s'], 'onChange is called with wrapTTL');+});+});<82990506+hc-github-team-secure-vault-core@users.noreply.github.com>(#27353)changelog/27346.txt | 3 +++ui/app/templates/components/transit-edit.hbs | 6 +++---ui/app/templates/components/transit-form-create.hbs | 2 +-ui/app/templates/components/transit-form-edit.hbs | 2 +-ui/app/templates/components/transit-form-show.hbs | 4 ++--ui/app/templates/components/transit-key-action/datakey.hbs | 2 +-ui/app/templates/components/transit-key-action/decrypt.hbs | 2 +-ui/app/templates/components/transit-key-action/encrypt.hbs | 2 +-ui/app/templates/components/transit-key-action/export.hbs | 2 +-ui/app/templates/components/transit-key-action/rewrap.hbs | 2 +-ui/app/templates/components/transit-key-action/sign.hbs | 2 +-ui/lib/core/addon/helpers/options-for-backend.js | 4 ++--12 files changed, 18 insertions(+), 15 deletions(-)create mode 100644 changelog/27346.txt
ui/tests/acceptance/aws-test.js+31 −19
@@ -3,16 +3,18 @@* SPDX-License-Identifier: BUSL-1.1*/-import { click, fillIn, findAll, currentURL, find, settled, waitUntil } from '@ember/test-helpers';+import { click, fillIn, currentURL, find, settled, waitUntil } from '@ember/test-helpers';import { module, test } from 'qunit';import { setupApplicationTest } from 'ember-qunit';import { v4 as uuidv4 } from 'uuid';import authPage from 'vault/tests/pages/auth';import enablePage from 'vault/tests/pages/settings/mount-secret-backend';+import { setupMirage } from 'ember-cli-mirage/test-support';module('Acceptance | aws secret backend', function (hooks) {setupApplicationTest(hooks);+setupMirage(hooks);hooks.beforeEach(function () {this.uid = uuidv4();@@ -20,9 +22,13 @@ module('Acceptance | aws secret backend', function (hooks) {});test('aws backend', async function (assert) {-assert.expect(12);const path = `aws-${this.uid}`;const roleName = 'awsrole';+this.server.post(`/${path}/creds/${roleName}`, (_, req) => {+const payload = JSON.parse(req.requestBody);+assert.deepEqual(payload, { role_arn: 'foobar' }, 'does not send TTL when unchecked');+return {};+});await enablePage.enable('aws', path);await settled();@@ -31,28 +37,25 @@ module('Acceptance | aws secret backend', function (hooks) {await click('[data-test-secret-backend-configure]');assert.strictEqual(currentURL(), `/vault/settings/secrets/configure/${path}`);-assert.ok(findAll('[data-test-aws-root-creds-form]').length, 'renders the empty root creds form');-assert.ok(findAll('[data-test-aws-link="root-creds"]').length, 'renders the root creds link');-assert.ok(findAll('[data-test-aws-link="leases"]').length, 'renders the leases config link');+assert.dom('[data-test-aws-root-creds-form]').exists();+assert.dom('[data-test-aws-link="root-creds"]').exists();+assert.dom('[data-test-aws-link="leases"]').exists();await fillIn('[data-test-aws-input="accessKey"]', 'foo');await fillIn('[data-test-aws-input="secretKey"]', 'bar');await click('[data-test-aws-input="root-save"]');-assert.ok(-find('[data-test-flash-message]').textContent.trim(),-`The backend configuration saved successfully!`-);+assert+.dom('[data-test-flash-message]:last-of-type [data-test-flash-message-body]')+.includesText(`The backend configuration saved successfully!`);await click('[data-test-aws-link="leases"]');await click('[data-test-aws-input="lease-save"]');--assert.ok(-find('[data-test-flash-message]').textContent.trim(),-`The backend configuration saved successfully!`-);+assert+.dom('[data-test-flash-message]:last-of-type [data-test-flash-message-body]')+.includesText(`The backend configuration saved successfully!`);await click('[data-test-backend-view-link]');@@ -60,10 +63,7 @@ module('Acceptance | aws secret backend', function (hooks) {await click('[data-test-secret-create]');-assert.ok(-find('[data-test-secret-header]').textContent.includes('AWS Role'),-`aws: renders the create page`-);+assert.dom('[data-test-secret-header]').includesText('AWS Role');await fillIn('[data-test-input="name"]', roleName);@@ -78,7 +78,19 @@ module('Acceptance | aws secret backend', function (hooks) {await click(`[data-test-secret-breadcrumb="${path}"] a`);assert.strictEqual(currentURL(), `/vault/secrets/${path}/list`);-assert.ok(findAll(`[data-test-secret-link="${roleName}"]`).length, `aws: role shows in the list`);+assert.dom(`[data-test-secret-link="${roleName}"]`).exists();++// check that generates credentials flow is correct+await click(`[data-test-secret-link="${roleName}"]`);+assert.dom('h1').hasText('Generate AWS Credentials');+assert.dom('[data-test-input="credentialType"]').hasValue('iam_user');+await fillIn('[data-test-input="credentialType"]', 'assumed_role');+await click('[data-test-ttl-toggle="TTL"]');+assert.dom('[data-test-ttl-toggle="TTL"]').isNotChecked();+await fillIn('[data-test-input="roleArn"]', 'foobar');+await click('[data-test-secret-generate]');+assert.dom('[data-test-warning]').exists('Shows access warning after generation');+await click('[data-test-secret-generate-back]');//and deleteawait click(`[data-test-secret-link="${roleName}"] [data-test-popup-menu-trigger]`);
Release delta 1.16.0-rc1 → 1.16.3 (contains the fix)
vault/audit_broker.go+6 −32
@@ -209,6 +209,9 @@ func (a *AuditBroker) GetHash(ctx context.Context, name string, input string) (s// LogRequest is used to ensure all the audit backends have an opportunity to// log the given request and that *at least one* succeeds.func (a *AuditBroker) LogRequest(ctx context.Context, in *logical.LogInput) (ret error) {+a.RLock()+defer a.RUnlock()+// If no backends are registered then we have no devices to log the request.if len(a.backends) < 1 {return nil@@ -216,19 +219,6 @@ func (a *AuditBroker) LogRequest(ctx context.Context, in *logical.LogInput) (retdefer metrics.MeasureSince([]string{"audit", "log_request"}, time.Now())-a.RLock()-defer a.RUnlock()--if in.Request.InboundSSCToken != "" {-if in.Auth != nil {-reqAuthToken := in.Auth.ClientToken-in.Auth.ClientToken = in.Request.InboundSSCToken-defer func() {-in.Auth.ClientToken = reqAuthToken-}()-}-}-var retErr *multierror.Errordefer func() {@@ -245,11 +235,6 @@ func (a *AuditBroker) LogRequest(ctx context.Context, in *logical.LogInput) (retmetrics.IncrCounter([]string{"audit", "log_request_failure"}, failure)}()-headers := in.Request.Headers-defer func() {-in.Request.Headers = headers-}()-e, err := audit.NewEvent(audit.RequestType)if err != nil {retErr = multierror.Append(retErr, err)@@ -299,6 +284,9 @@ func (a *AuditBroker) LogRequest(ctx context.Context, in *logical.LogInput) (ret// LogResponse is used to ensure all the audit backends have an opportunity to// log the given response and that *at least one* succeeds.func (a *AuditBroker) LogResponse(ctx context.Context, in *logical.LogInput) (ret error) {+a.RLock()+defer a.RUnlock()+// If no backends are registered then we have no devices to send audit entries to.if len(a.backends) < 1 {return nil@@ -306,15 +294,6 @@ func (a *AuditBroker) LogResponse(ctx context.Context, in *logical.LogInput) (redefer metrics.MeasureSince([]string{"audit", "log_response"}, time.Now())-a.RLock()-defer a.RUnlock()--if in.Request.InboundSSCToken != "" && in.Auth != nil {-reqAuthToken := in.Auth.ClientToken-in.Auth.ClientToken = in.Request.InboundSSCToken-defer func() { in.Auth.ClientToken = reqAuthToken }()-}-var retErr *multierror.Errordefer func() {@@ -331,11 +310,6 @@ func (a *AuditBroker) LogResponse(ctx context.Context, in *logical.LogInput) (remetrics.IncrCounter([]string{"audit", "log_response_failure"}, failure)}()-headers := in.Request.Headers-defer func() {-in.Request.Headers = headers-}()-e, err := audit.NewEvent(audit.ResponseType)if err != nil {retErr = multierror.Append(retErr, err)<82990506+hc-github-team-secure-vault-core@users.noreply.github.com>734afbe09e0f35fb01e01c59f0eae879fcb7bce0 (#25463)builtin/credential/cert/backend.go | 46 ++++++++++++++++++++--builtin/credential/cert/path_certs.go | 10 +++--builtin/credential/cert/path_config.go | 20 ++++++++--builtin/credential/cert/path_crls.go | 3 ++builtin/credential/cert/path_login.go | 32 ++++++++++++---builtin/credential/cert/path_login_test.go | 17 ++++++++changelog/25421.txt | 3 ++go.mod | 1 +go.sum | 2 +website/content/api-docs/auth/cert.mdx | 2 +10 files changed, 120 insertions(+), 16 deletions(-)create mode 100644 changelog/25421.txt
ui/app/models/transform.js+45 −5
@@ -8,8 +8,7 @@ import { computed } from '@ember/object';import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities';import { expandAttributeMeta } from 'vault/utils/field-to-attrs';-// these arrays define the order in which the fields will be displayed-// see+// these arrays define the order in which the fields will be displayed, see:// https://developer.hashicorp.com/vault/api-docs/secret/transform#create-update-transformation-deprecated-1-6const TYPES = [{@@ -20,6 +19,10 @@ const TYPES = [value: 'masking',displayName: 'Masking',},+{+value: 'tokenization',+displayName: 'Tokenization',+},];const TWEAK_SOURCE = [@@ -83,12 +86,49 @@ export default Model.extend({subText: 'Search for an existing role, type a new role to create it, or use a wildcard (*).',wildcardLabel: 'role',}),+deletion_allowed: attr('boolean', {+label: 'Allow deletion',+subText:+'If checked, this transform can be deleted otherwise deletion is blocked. Note that deleting the transform deletes the underlying key which makes decoding of tokenized values impossible without restoring from a backup.',+}),+convergent: attr('boolean', {+label: 'Use convergent tokenization',+subText:+"This cannot be edited later. If checked, tokenization of the same plaintext more than once results in the same token. Defaults to false as unique tokens are more desirable from a security standpoint if there isn't a use-case need for convergence.",+}),+stores: attr('array', {+label: 'Stores',+editType: 'stringArray',+subText:+"The list of tokenization stores to use for tokenization state. Vault's internal storage is used by default.",+}),+mapping_mode: attr('string', {+defaultValue: 'default',+subText:+'Specifies the mapping mode for stored tokenization values. "default" is strongly recommended for highest security, "exportable" allows for all plaintexts to be decoded via the export-decoded endpoint in an emergency.',+}),+max_ttl: attr({+editType: 'ttl',+defaultValue: '0',+label: 'Maximum TTL (time-to-live) of a token',+helperTextDisabled: 'If "0" or unspecified, tokens may have no expiration.',+}),+transformAttrs: computed('type', function () {-if (this.type === 'masking') {-return ['name', 'type', 'masking_character', 'template', 'allowed_roles'];+// allowed_roles not included so it displays at the bottom of the form+const baseAttrs = ['name', 'type', 'deletion_allowed'];+switch (this.type) {+case 'fpe':+return [...baseAttrs, 'tweak_source', 'template', 'allowed_roles'];+case 'masking':+return [...baseAttrs, 'masking_character', 'template', 'allowed_roles'];+case 'tokenization':+return [...baseAttrs, 'mapping_mode', 'convergent', 'max_ttl', 'stores', 'allowed_roles'];+default:+return [...baseAttrs];}-return ['name', 'type', 'tweak_source', 'template', 'allowed_roles'];}),+transformFieldAttrs: computed('transformAttrs', function () {return expandAttributeMeta(this, this.transformAttrs);}),
ui/app/models/auth-method.js+52 −53
@@ -4,14 +4,13 @@*/import Model, { belongsTo, hasMany, attr } from '@ember-data/model';-import { alias } from '@ember/object/computed'; // eslint-disable-line-import { computed } from '@ember/object'; // eslint-disable-lineimport { inject as service } from '@ember/service';import fieldToAttrs, { expandAttributeMeta } from 'vault/utils/field-to-attrs';import apiPath from 'vault/utils/api-path';-import attachCapabilities from 'vault/lib/attach-capabilities';import { withModelValidations } from 'vault/decorators/model-validations';import { allMethods } from 'vault/helpers/mountable-auth-methods';+import lazyCapabilities from 'vault/macros/lazy-capabilities';+import { action } from '@ember/object';const validations = {path: [@@ -25,51 +24,51 @@ const validations = {],};-// unsure if ember-api-actions will work on native JS class model-// for now create class to use validations and then use classic extend pattern@withModelValidations(validations)-class AuthMethodModel extends Model {}-const ModelExport = AuthMethodModel.extend({-store: service(),+export default class AuthMethodModel extends Model {+@service store;-config: belongsTo('mount-config', { async: false, inverse: null }), // one-to-none that replaces former fragment-authConfigs: hasMany('auth-config', { polymorphic: true, inverse: 'backend', async: false }),-path: attr('string'),-accessor: attr('string'),-name: attr('string'),-type: attr('string'),+@belongsTo('mount-config', { async: false, inverse: null }) config; // one-to-none that replaces former fragment+@hasMany('auth-config', { polymorphic: true, inverse: 'backend', async: false }) authConfigs;+@attr('string') path;+@attr('string') accessor;+@attr('string') name;+@attr('string') type;// namespaces introduced types with a `ns_` prefix for built-in engines// so we need to strip that to normalize the type-methodType: computed('type', function () {+get methodType() {return this.type.replace(/^ns_/, '');-}),-icon: computed('methodType', function () {+}+get icon() {const authMethods = allMethods().find((backend) => backend.type === this.methodType);return authMethods?.glyph || 'users';-}),-description: attr('string', {+}+@attr('string', {editType: 'textarea',-}),-local: attr('boolean', {+})+description;+@attr('boolean', {helpText:'When Replication is enabled, a local mount will not be replicated across clusters. This can only be specified at mount time.',-}),-sealWrap: attr('boolean', {+})+local;+@attr('boolean', {helpText:'When enabled - if a seal supporting seal wrapping is specified in the configuration, all critical security parameters (CSPs) in this backend will be seal wrapped. (For KV mounts, all values will be seal wrapped.) This can only be specified at mount time.',-}),+})+sealWrap;// used when the `auth` prefix is important,// currently only when setting perf mount filtering-apiPath: computed('path', function () {+get apiPath() {return `auth/${this.path}`;-}),-localDisplay: computed('local', function () {+}+get localDisplay() {return this.local ? 'local' : 'replicated';-}),+}-tuneAttrs: computed('path', function () {+get tuneAttrs() {const { methodType } = this;let tuneAttrs;// token_type should not be tuneable for the token auth method@@ -85,9 +84,9 @@ const ModelExport = AuthMethodModel.extend({];}return expandAttributeMeta(this, tuneAttrs);-}),+}-formFields: computed(function () {+get formFields() {return ['type','path',@@ -97,9 +96,9 @@ const ModelExport = AuthMethodModel.extend({'sealWrap','config.{listingVisibility,defaultLeaseTtl,maxLeaseTtl,tokenType,auditNonHmacRequestKeys,auditNonHmacResponseKeys,passthroughRequestHeaders}',];-}),+}-formFieldGroups: computed(function () {+get formFieldGroups() {return [{ default: ['path'] },{@@ -112,30 +111,30 @@ const ModelExport = AuthMethodModel.extend({],},];-}),+}-attrs: computed('formFields', function () {+get attrs() {return expandAttributeMeta(this, this.formFields);-}),+}-fieldGroups: computed('formFieldGroups', function () {+get fieldGroups() {return fieldToAttrs(this, this.formFieldGroups);-}),-canDisable: alias('deletePath.canDelete'),-canEdit: alias('configPath.canUpdate'),+}+@lazyCapabilities(apiPath`sys/auth/${'id'}`, 'id') deletePath;+@lazyCapabilities(apiPath`auth/${'id'}/config`, 'id') configPath;+@lazyCapabilities(apiPath`auth/${'id'}/config/client`, 'id') awsConfigPath;+get canDisable() {+return this.deletePath.get('canDelete') !== false;+}+get canEdit() {+return this.configPath.get('canUpdate') !== false;+}+get canEditAws() {+return this.awsConfigPath.get('canUpdate') !== false;+}+@actiontune(data) {return this.store.adapterFor('auth-method').tune(this.path, data);-},-});--export default attachCapabilities(ModelExport, {-deletePath: apiPath`sys/auth/${'id'}`,-configPath: function (context) {-if (context.type === 'aws') {-return apiPath`auth/${'id'}/config/client`.call(this, context);-} else {-return apiPath`auth/${'id'}/config`.call(this, context);-}-},-});+}+}
builtin/credential/cert/path_config.go+17 −3
@@ -11,7 +11,7 @@ import ("github.com/hashicorp/vault/sdk/logical")-const maxCacheSize = 100000+const maxOcspCacheSize = 100000func pathConfig(b *backend) *framework.Path {return &framework.Path{@@ -37,6 +37,11 @@ func pathConfig(b *backend) *framework.Path {Default: 100,Description: `The size of the in memory OCSP response cache, shared by all configured certs`,},+"role_cache_size": {+Type: framework.TypeInt,+Default: defaultRoleCacheSize,+Description: `The size of the in memory role cache`,+},},Operations: map[logical.Operation]framework.OperationHandler{@@ -70,11 +75,18 @@ func (b *backend) pathConfigWrite(ctx context.Context, req *logical.Request, dat}if cacheSizeRaw, ok := data.GetOk("ocsp_cache_size"); ok {cacheSize := cacheSizeRaw.(int)-if cacheSize < 2 || cacheSize > maxCacheSize {-return logical.ErrorResponse("invalid cache size, must be >= 2 and <= %d", maxCacheSize), nil+if cacheSize < 2 || cacheSize > maxOcspCacheSize {+return logical.ErrorResponse("invalid ocsp cache size, must be >= 2 and <= %d", maxOcspCacheSize), nil}config.OcspCacheSize = cacheSize}+if cacheSizeRaw, ok := data.GetOk("role_cache_size"); ok {+cacheSize := cacheSizeRaw.(int)+if (cacheSize < 0 && cacheSize != -1) || cacheSize > maxRoleCacheSize {+return logical.ErrorResponse("invalid role cache size, must be <= %d or -1 to disable role caching", maxRoleCacheSize), nil+}+config.RoleCacheSize = cacheSize+}if err := b.storeConfig(ctx, req.Storage, config); err != nil {return nil, err}@@ -91,6 +103,7 @@ func (b *backend) pathConfigRead(ctx context.Context, req *logical.Request, d *f"disable_binding": cfg.DisableBinding,"enable_identity_alias_metadata": cfg.EnableIdentityAliasMetadata,"ocsp_cache_size": cfg.OcspCacheSize,+"role_cache_size": cfg.RoleCacheSize,}return &logical.Response{@@ -119,4 +132,5 @@ type config struct {DisableBinding bool `json:"disable_binding"`EnableIdentityAliasMetadata bool `json:"enable_identity_alias_metadata"`OcspCacheSize int `json:"ocsp_cache_size"`+RoleCacheSize int `json:"role_cache_size"`}
website/content/docs/concepts/filtering/index.mdx+179 −0
@@ -0,0 +1,179 @@+---+layout: docs+page_title: Filtering+description: >-+An introduction to the filtering syntax used in Vault.+---++# Filter expressions in Vault++Filter expressions use matching operators and selector values to parse+out important or relevant information. In some situations, you can use filter+expressions to control how Vault processes results.++## Filter expression syntax++Basic filter expressions are always written in plain text with a+**matching operator**, a **selector**, and a **selector value**.++- the **matching operator** tells Vault how to compare the selector and selector+value.+- the **selector** is a [JSON pointer](https://tools.ietf.org/html/rfc6901) that+indicates which field or parameter in a JSON object to consider.+- the **selector value** is a JSON pointer, number, or string that defines a+pattern Vault can filter against.++For example, in the filter expression:++```text+product/name == "Vault"+```++- Equality (`==`) is the matching operator.+- The JSON pointer `product/name` is the selector.+- The string "Vault" is the selector value.++Complex filter expressions also allow Boolean logic and parenthesis. For example:++```text+(product/name == "Vault") and (timestamp < "2024-02-01")+```++When parsing filter expressions, Vault ignores whitespace unless the whitespace+is part of a literal string.++Filter expression+`product/name=="Vault"` and `product/name == "Vault"` generate the same results+while `product/name == " Vault "` and `product/name == "Vault"` generate+different results.++<Note title="Selectors are not universal">++Filtering-enabled endpoints can support different selectors. Make sure to+consult the API documentation for a given endpoint when constructing your+filter expressions.++</Note>+<Tabs>++<Tab heading="Matching operators">++++```text+// Equality & Inequality checks+<Selector> == "<Value>"+<Selector> != "<Value>"++// Emptiness checks+<Selector> is empty+<Selector> is not empty++// Contains checks or Substring Matching+"<Value>" in <Selector>+"<Value>" not in <Selector>+<Selector> contains "<Value>"+<Selector> not contains "<Value>"++// Regular Expression Matching+<Selector> matches "<Value>"+<Selector> not matches "<Value>"+```++</Tab>++<Tab heading="Selectors">+++Selectors must be valid JSON pointers enclosed in quotes with a leading slash (`/`).+++JSON pointers use forward slashes to define paths through a JSON block. For+example, to target the product name in:++```json+{ "product":+{+"name": "Vault",+"version": "1.16.0"+},+{+"name": "Boundary",+"version": "0.15.0"+}+}++```++The selector would be `/product/name`.+++</Tab>++<Tab heading="Selector values">+++Selector values can be any valid selector, integer, floating point number, or+string. Numbers and strings should be quoted in double quotes or backticks.++Strings quoted in backticks are treated as literal values and escape sequences+like `\n` are not expanded.++| Value | Type | Expanded value |+|-------------------|---------|-------------------|+| "Vault\tBoundary" | string | "Vault Boundary" |+| `Vault\tBoundary` | string | "Vault\tBoundary" |+| "10" | integer | "10" |+| `10` | integer | "10" |+| "0.75" | float | "0.75" |+++</Tab>++</Tabs>++## Complex expressions++Complex expressions combine basic expressions with logical operators, grouping, and matching expressions.++```text+// Logical Or - evaluates to true if either sub-expression does+<Expression 1> or <Expression 2>++// Logical And - evaluates to true if both sub-expressions do+<Expression 1 > and <Expression 2>++// Logical Not - evaluates to true if the sub-expression does not+not <Expression 1>++// Grouping - Overrides normal precedence rules+( <Expression 1> )++// Inspects data to check for a match+<Matching Expression 1>+```++Vault uses standard operator precedence when resolving complex+expressions. For example, the expression+`<Expression 1> and not <Expression 2> or <Expression 3>` resolves+the same as+`( <Expression 1> and (not <Expression 2> )) or <Expression 3>`.+++## Performance++Filters consume a portion of CPU time on the Vault node where they run.++<Note title="Regular expressions">+Using multiple/complex expressions including regular expressions+(regex) will have a larger impact on performance than fewer/simpler filters.+</Note>++Always test your filters in pre-production environments to ensure correctness.++Ideally you should [codify your management of Vault](/vault/tutorials/operations/codify-mgmt-vault-terraform)+using tools such as [Terraform](https://www.terraform.io/), to prevent accidentally enabling an audit device+in a production environment with untested/incorrect settings.++Finally, always ensure you profile production-like workloads within your pre-production+environments in order to accurately assess the performance of Vault.
website/content/docs/concepts/events.mdx+39 −38
@@ -24,37 +24,37 @@ additional `metadata` field.The following events are currently generated by Vault and its builtin plugins automatically:-| Plugin | Event Type | Metadata | Vault version |-| -------- | ------------------------------------ | ---------------------------------------------- | ------------- |-| database | `database/config-delete` | `modified`, `operation`, `path`, `name` | 1.16 |-| database | `database/config-write` | `modified`, `operation`, `path`, `name` | 1.16 |-| database | `database/creds-create` | `modified`, `operation`, `path`, `name` | 1.16 |-| database | `database/reload` | `modified`, `operation`, `path`, `plugin_name` | 1.16 |-| database | `database/reset` | `modified`, `operation`, `path`, `name` | 1.16 |-| database | `database/role-create` | `modified`, `operation`, `path`, `name` | 1.16 |-| database | `database/role-delete` | `modified`, `operation`, `path`, `name` | 1.16 |-| database | `database/role-update` | `modified`, `operation`, `path`, `name` | 1.16 |-| database | `database/root-rotate-fail` | `modified`, `operation`, `path`, `name` | 1.16 |-| database | `database/root-rotate` | `modified`, `operation`, `path`, `name` | 1.16 |-| database | `database/rotate-fail` | `modified`, `operation`, `path`, `name` | 1.16 |-| database | `database/rotate` | `modified`, `operation`, `path`, `name` | 1.16 |-| database | `database/static-creds-create-fail` | `modified`, `operation`, `path`, `name` | 1.16 |-| database | `database/static-creds-create` | `modified`, `operation`, `path`, `name` | 1.16 |-| database | `database/static-role-create` | `modified`, `operation`, `path`, `name` | 1.16 |-| database | `database/static-role-delete` | `modified`, `operation`, `path`, `name` | 1.16 |-| database | `database/static-role-update` | `modified`, `operation`, `path`, `name` | 1.16 |-| kv | `kv-v1/delete` | `modified`, `operation`, `path` | 1.13 |-| kv | `kv-v1/write` | `data_path`, `modified`, `operation`, `path` | 1.13 |-| kv | `kv-v2/config-write` | `data_path`, `modified`, `operation`, `path` | 1.13 |-| kv | `kv-v2/data-delete` | `modified`, `operation`, `path` | 1.13 |-| kv | `kv-v2/data-patch` | `data_path`, `modified`, `operation`, `path` | 1.13 |-| kv | `kv-v2/data-write` | `data_path`, `modified`, `operation`, `path` | 1.13 |-| kv | `kv-v2/delete` | `modified`, `operation`, `path` | 1.13 |-| kv | `kv-v2/destroy` | `modified`, `operation`, `path` | 1.13 |-| kv | `kv-v2/metadata-delete` | `modified`, `operation`, `path` | 1.13 |-| kv | `kv-v2/metadata-patch` | `data_path`, `modified`, `operation`, `path` | 1.13 |-| kv | `kv-v2/metadata-write` | `data_path`, `modified`, `operation`, `path` | 1.13 |-| kv | `kv-v2/undelete` | `data_path`, `modified`, `operation`, `path` | 1.13 |+| Plugin | Event Type | Metadata | Vault version |+|----------|-------------------------------------|------------------------------------------------|---------------|+| database | `database/config-delete` | `modified`, `operation`, `path`, `name` | 1.16 |+| database | `database/config-write` | `modified`, `operation`, `path`, `name` | 1.16 |+| database | `database/creds-create` | `modified`, `operation`, `path`, `name` | 1.16 |+| database | `database/reload` | `modified`, `operation`, `path`, `plugin_name` | 1.16 |+| database | `database/reset` | `modified`, `operation`, `path`, `name` | 1.16 |+| database | `database/role-create` | `modified`, `operation`, `path`, `name` | 1.16 |+| database | `database/role-delete` | `modified`, `operation`, `path`, `name` | 1.16 |+| database | `database/role-update` | `modified`, `operation`, `path`, `name` | 1.16 |+| database | `database/root-rotate-fail` | `modified`, `operation`, `path`, `name` | 1.16 |+| database | `database/root-rotate` | `modified`, `operation`, `path`, `name` | 1.16 |+| database | `database/rotate-fail` | `modified`, `operation`, `path`, `name` | 1.16 |+| database | `database/rotate` | `modified`, `operation`, `path`, `name` | 1.16 |+| database | `database/static-creds-create-fail` | `modified`, `operation`, `path`, `name` | 1.16 |+| database | `database/static-creds-create` | `modified`, `operation`, `path`, `name` | 1.16 |+| database | `database/static-role-create` | `modified`, `operation`, `path`, `name` | 1.16 |+| database | `database/static-role-delete` | `modified`, `operation`, `path`, `name` | 1.16 |+| database | `database/static-role-update` | `modified`, `operation`, `path`, `name` | 1.16 |+| kv | `kv-v1/delete` | `modified`, `operation`, `path` | 1.13 |+| kv | `kv-v1/write` | `data_path`, `modified`, `operation`, `path` | 1.13 |+| kv | `kv-v2/config-write` | `data_path`, `modified`, `operation`, `path` | 1.13 |+| kv | `kv-v2/data-delete` | `modified`, `operation`, `path` | 1.13 |+| kv | `kv-v2/data-patch` | `data_path`, `modified`, `operation`, `path` | 1.13 |+| kv | `kv-v2/data-write` | `data_path`, `modified`, `operation`, `path` | 1.13 |+| kv | `kv-v2/delete` | `modified`, `operation`, `path` | 1.13 |+| kv | `kv-v2/destroy` | `modified`, `operation`, `path` | 1.13 |+| kv | `kv-v2/metadata-delete` | `modified`, `operation`, `path` | 1.13 |+| kv | `kv-v2/metadata-patch` | `data_path`, `modified`, `operation`, `path` | 1.13 |+| kv | `kv-v2/metadata-write` | `data_path`, `modified`, `operation`, `path` | 1.13 |+| kv | `kv-v2/undelete` | `data_path`, `modified`, `operation`, `path` | 1.13 |## Event format@@ -169,7 +169,7 @@ To subscribe to an event, you must have the following policy grants:}```-1. `list` and `subscribe` capabilities on the *path of the secret* for events+2. `list` and `subscribe` capabilities on the *path of the secret* for eventsrelated to secrets. The policy must also provide a `subscribe_event_types`entry with the specific events subscribers are allowed to use. For example,to receive events related to the KV secrets engine path,@@ -195,12 +195,12 @@ revoked or a policy is deleted.## Supported versions-Version | Support-<= 1.12 | Not supported-1.13 | Supported; **disabled** by default-1.14 | Supported; **disabled** by default-1.15+ | Supported; **enabled** by default+| Version | Support |+|---------|------------------------------------|+| <= 1.12 | Not supported |+| 1.13 | Supported; **disabled** by default |+| 1.14 | Supported; **disabled** by default |+| 1.15+ | Supported; **enabled** by default |For versions where events are disabled by default, you can enable thefunctionality with the `events.alpha1`@@ -209,4 +209,4 @@ configuration or from the command line with the `-experiments` flag. For example```shell-session$ vault server -experiment events.alpha1-```+```
command/agent_test.go+155 −0
@@ -5,9 +5,11 @@ package commandimport ("bufio"+"context""crypto/tls""crypto/x509""encoding/json"+"errors""fmt""io""net"@@ -3204,6 +3206,159 @@ auto_auth {require.Truef(t, found, "unable to find consul-template partial message in logs", runnerLogMessage)}+// TestAgent_DeleteAfterVersion_Rendering Validates that Vault Agent+// can correctly render a secret with delete_after_version set.+func TestAgent_DeleteAfterVersion_Rendering(t *testing.T) {+logger := logging.NewVaultLogger(hclog.Trace)+cluster := vault.NewTestCluster(t,+&vault.CoreConfig{+Logger: logger,+},+&vault.TestClusterOptions{+NumCores: 1,+HandlerFunc: vaulthttp.Handler,+})+cluster.Start()+defer cluster.Cleanup()++vault.TestWaitActive(t, cluster.Cores[0].Core)+serverClient := cluster.Cores[0].Client++// Set up KVv2+err := serverClient.Sys().Mount("kv-v2", &api.MountInput{+Type: "kv-v2",+})+require.NoError(t, err)++// Configure the mount to set delete_version_after on all of its secrets+_, err = serverClient.Logical().Write("kv-v2/config", map[string]interface{}{+"delete_version_after": "1h",+})+require.NoError(t, err)++// Set up the secret (which will have delete_version_after set to 1h)+data, err := serverClient.KVv2("kv-v2").Put(context.Background(), "foo", map[string]interface{}{+"bar": "baz",+})+require.NoError(t, err)++// Ensure Deletion Time was correctly set+require.NotZero(t, data.VersionMetadata.DeletionTime)+require.True(t, data.VersionMetadata.DeletionTime.After(time.Now()))+require.NotNil(t, data.VersionMetadata.CreatedTime)+require.True(t, data.VersionMetadata.DeletionTime.After(data.VersionMetadata.CreatedTime))++// Unset the environment variable so that Agent picks up the right test+// cluster address+defer os.Setenv(api.EnvVaultAddress, os.Getenv(api.EnvVaultAddress))+os.Setenv(api.EnvVaultAddress, serverClient.Address())++// create temp dir for this test run+tmpDir, err := os.MkdirTemp("", "TestAgent_DeleteAfterVersion_Rendering")+require.NoError(t, err)++tokenFileName := makeTempFile(t, "token-file", serverClient.Token())+defer os.Remove(tokenFileName)++autoAuthConfig := fmt.Sprintf(`+auto_auth {+method {+type = "token_file"+config = {+token_file_path = "%s"+}+}+}`, tokenFileName)++// Create a config file+config := `+vault {+address = "%s"+tls_skip_verify = true+}++%s++%s+`++fileName := "secret.txt"+templateConfig := fmt.Sprintf(`+template {+destination = "%s/%s"+contents = "{{ with secret \"kv-v2/foo\" }}{{ .Data.data.bar }}{{ end }}"+}+`, tmpDir, fileName)++config = fmt.Sprintf(config, serverClient.Address(), autoAuthConfig, templateConfig)+configPath := makeTempFile(t, "config.hcl", config)+defer os.Remove(configPath)++// Start the agent+ui, cmd := testAgentCommand(t, logger)+cmd.client = serverClient+cmd.startedCh = make(chan struct{})++wg := &sync.WaitGroup{}+wg.Add(1)+go func() {+code := cmd.Run([]string{"-config", configPath})+if code != 0 {+t.Errorf("non-zero return code when running agent: %d", code)+t.Logf("STDOUT from agent:\n%s", ui.OutputWriter.String())+t.Logf("STDERR from agent:\n%s", ui.ErrorWriter.String())+}+wg.Done()+}()++select {+case <-cmd.startedCh:+case <-time.After(5 * time.Second):+t.Errorf("timeout")+}++// We need to shut down the Agent command+defer func() {+cmd.ShutdownCh <- struct{}{}+wg.Wait()+}()++filePath := fmt.Sprintf("%s/%s", tmpDir, fileName)++waitForFiles := func() error {+tick := time.Tick(100 * time.Millisecond)+timeout := time.After(10 * time.Second)+// We need to wait for the templates to render...+for {+select {+case <-timeout:+t.Fatalf("timed out waiting for templates to render, last error: %v", err)+case <-tick:+}++_, err := os.Stat(filePath)+if err != nil {+if errors.Is(err, os.ErrNotExist) {+continue+}+return err+}++return nil+}+}++err = waitForFiles()+require.NoError(t, err)++// Ensure the file has the+fileData, err := os.ReadFile(filePath)+require.NoError(t, err)+if string(fileData) != "baz" {+t.Fatalf("Unexpected file contents. Expected 'baz', got %s", string(fileData))+}+}+// Get a randomly assigned port and then free it again before returning it.// There is still a race when trying to use it, but should work better// than a static port.
ui/tests/acceptance/auth-list-test.js+12 −3
@@ -75,7 +75,7 @@ module('Acceptance | auth backend list', function (hooks) {});test('auth methods are linkable and link to correct view', async function (assert) {-assert.expect(16);+assert.expect(24);const uid = uuidv4();await visit('/vault/access');@@ -83,15 +83,22 @@ module('Acceptance | auth backend list', function (hooks) {const backends = supportedAuthBackends();for (const backend of backends) {const { type } = backend;-const path = `auth-list-${type}-${uid}`;+const path = type === 'token' ? 'token' : `auth-list-${type}-${uid}`;if (type !== 'token') {await enablePage.enable(type, path);}await settled();await visit('/vault/access');+// check popup menu+const itemCount = type === 'token' ? 2 : 3;+await click(`[data-test-auth-backend-link="${path}"] [data-test-popup-menu-trigger]`);+assert+.dom('.hds-dropdown-list-item')+.exists({ count: itemCount }, `shows ${itemCount} dropdown items for ${type}`);+// all auth methods should be linkable-await click(`[data-test-auth-backend-link="${type === 'token' ? type : path}"]`);+await click(`[data-test-auth-backend-link="${path}"]`);if (!supportManaged.includes(type)) {assert.dom('[data-test-auth-section-tab]').exists({ count: 1 });assert@@ -106,6 +113,8 @@ module('Acceptance | auth backend list', function (hooks) {assert.dom('[data-test-auth-section-tab]').exists({ count: expectedTabs }, `has management tabs for ${type} auth method`);+}+if (type !== 'token') {// cleanup methodawait runCmd(deleteAuthCmd(path));}<82990506+hc-github-team-secure-vault-core@users.noreply.github.com>2f2e0184bb57996c0e94955173d6dc6556ff30bf (#25423)changelog/25399.txt | 3 ++ui/app/styles/helper-classes/layout.scss | 2 +-.../addon/components/certificate-card.hbs | 6 +--.../core/addon/components/certificate-card.js | 29 +++++++++----.../components/page/pki-issuer-details.hbs | 2 +-.../components/certificate-card-test.js | 43 ++++++++++++++-----6 files changed, 60 insertions(+), 25 deletions(-)create mode 100644 changelog/25399.txt
website/content/docs/enterprise/license/manual-reporting.mdx+173 −0
@@ -0,0 +1,173 @@+---+layout: docs+page_title: Manual license utilization reporting+description: >-+Manual license utilization reporting allows you to export, review, and send license utilization data to HashiCorp through the CLI or HCP Web Portal.+---++# Manual license utilization reporting++@include 'alerts/enterprise-only.mdx'++Manual license utilization reporting allows you to export, review, and send+license utilization data to HashiCorp via the CLI or HCP Web Portal. Use these+reports to understand how much more you can deploy under your current contract,+protect against overutilization, and budget for predicted consumption. Manual+reporting shares the minimum data required to validate license utilization as+defined in our contracts. The reports consist of mostly computed metrics and+will never contain Personal Identifiable Information (PII) or other sensitive+information.++Manual license utilization shares the same data as automated license utilization+but is more time consuming. Unless you are running in an air-gapped environment+or have another reason to report data manually, we strongly recommend using+automated reporting instead. If you have disabled automated license reporting,+you can re-enable it by reversing the opt-out process described in the+[documentation](/vault/docs/enterprise/license/utilization-reporting#opt-out).++If you are considering manual reporting because you’re worried about your data,+we strongly recommend that you review the [example+payloads](#data-file-content), which are the same for automated and manual+reporting. If you have further concerns with any of the automatically-reported+data please bring them to your account manager before opting out of automated+reporting in favor of manual reporting.++## How to manually send data reports++### Generate a data bundle++Data bundles include collections of JSON snapshots that contain license+utilization information.++1. Login into your [cluster node](/vault/tutorials/cloud/vault-access-cluster).+1. Run this CLI command to generate a data bundle:++```shell-session+$ vault operator utilization+```++By default, the bundle will include all historical snapshots.++You can provide context about the conditions under which the report was+generated and submitted by providing a comment. This optional comment will+not be included in the license utilization bundle, but will be included in+the Vault server logs.++**Example:**++```shell-session+$ vault operator utilization -message=”Change Control 654987” \+-output=”/utilization/reports/latest.json”+```++This command will export all the persisted snapshots into a bundle. The+message “Change Control 654987” will not be included in the bundle but will+be included in Vault server logs. The `-output` flags specifies the output+location of the JSON bundle.++**Available command flags:**++- `-message` `(string: “”)` - Provide context about the conditions under+which the report was generated and submitted. This message is not included+in the license utilization bundle but will be included in the vault server+logs. (optional)++- `-today-only` `(bool: false)` - To include only today’s snapshot, no+historical snapshots. If no snapshots were persisted in the last 24 hrs, it+takes a snapshot and exports it to a bundle. (optional)++- `-output` `(string: “”)` - Specifies the output path for the bundle.+Defaults to a time-based generated file name. (optional)+++### Send the data bundle to HashiCorp++1. Go to https://portal.cloud.hashicorp.com/license-utilization/reports/create+1. Click on **Choose files**, or drop your file(s) into the container.++a. If the upload succeeded, the HCP user interface will change the file+status to **Uploaded** in green.++b. If the upload failed, the file status will say **Failed** in red, and+will include error information.++If the upload fails make sure you haven’t modified the file signature. If the+error persists, please contact your account representative.+++## Enable manual reporting++Upgrade to a release that supports manual license utilization reporting. These+releases include:++- Vault Enterprise 1.16.0 and later+- Vault Enterprise 1.15.6 and later+- Vault Enterprise 1.14.10 and later++## Configuration++Administrators can manage disk space for storing snapshots by defining the+number of days snapshots can be retained.++```hcl+reporting {+snapshot_retention_time = "2400h"+}+```++The default retention period is 400 days.++## Data file content++<CodeBlockConfig hideClipboard>++```json+{+"snapshot_version": 2,+"id": "0001JWAY00BRF8TEXC9CVRHBAC",+"timestamp": "2024-02-08T16:55:28.085215-08:00",+"schema_version": "2.0.0",+"product": "vault",+"process_id": "01HP5NJS21HN50FY0CBS0SYGCH",+"metrics": {+"clientcount.current_month_estimate.type.entity": {+"key": "clientcount.current_month_estimate.type.entity",+"value": 20,+"mode": "write"+},+"clientcount.current_month_estimate.type.nonentity": {+"key": "clientcount.current_month_estimate.type.nonentity",+"value": 11,+"mode": "write"+},+"clientcount.current_month_estimate.type.secret_sync": {+"key": "clientcount.current_month_estimate.type.secret_sync",+"value": 0,+"mode": "write"+},+"clientcount.previous_month_complete.type.entity": {+"key": "clientcount.previous_month_complete.type.entity",+"value": 0,+"mode": "write"+},+"clientcount.previous_month_complete.type.nonentity": {+"key": "clientcount.previous_month_complete.type.nonentity",+"value": 0,+"mode": "write"+},+"clientcount.previous_month_complete.type.secret_sync": {+"key": "clientcount.previous_month_complete.type.secret_sync",+"value": 0,+"mode": "write"+}+},+"product_version": "1.16.0+ent",+"license_id": "7d68b16a-74fe-3b9f-a1a7-08cf461fff1c",+"checksum": 6861637915450723051,+"metadata": {+"billing_start": "2023-05-04T00:00:00Z",+"cluster_id": "16d0ff5b-9d40-d7a7-384c-c9b95320c60e"+}+```++</CodeBlockConfig>
audit/entry_formatter.go+7 −0
@@ -107,6 +107,13 @@ func (f *EntryFormatter) Process(ctx context.Context, e *eventlogger.Event) (*evdata.Request.Headers = adjustedHeaders}+// If the request contains a Server-Side Consistency Token (SSCT), and we+// have an auth response, overwrite the existing client token with the SSCT,+// so that the SSCT appears in the audit log for this entry.+if data.Request != nil && data.Request.InboundSSCToken != "" && data.Auth != nil {+data.Auth.ClientToken = data.Request.InboundSSCToken+}+var result []byteswitch a.Subtype {
audit/event.go+36 −3
@@ -5,10 +5,42 @@ package auditimport ("fmt"+"time""github.com/hashicorp/vault/internal/observability/event"+"github.com/hashicorp/vault/sdk/logical")+// version defines the version of audit events.+const version = "v0.1"++// Audit subtypes.+const (+RequestType subtype = "AuditRequest"+ResponseType subtype = "AuditResponse"+)++// Audit formats.+const (+JSONFormat format = "json"+JSONxFormat format = "jsonx"+)++// AuditEvent is the audit event.+type AuditEvent struct {+ID string `json:"id"`+Version string `json:"version"`+Subtype subtype `json:"subtype"` // the subtype of the audit event.+Timestamp time.Time `json:"timestamp"`+Data *logical.LogInput `json:"data"`+}++// format defines types of format audit events support.+type format string++// subtype defines the type of audit event.+type subtype string+// NewEvent should be used to create an audit event. The subtype field is needed// for audit events. It will generate an ID if no ID is supplied. Supported// options: WithID, WithNow.@@ -99,13 +131,14 @@ func (f format) String() string {}// MetricTag returns a tag corresponding to this subtype to include in metrics.-func (st subtype) MetricTag() string {-switch st {+// If a tag cannot be found the value is returned 'as-is' in string format.+func (t subtype) MetricTag() string {+switch t {case RequestType:return "log_request"case ResponseType:return "log_response"}-return ""+return string(t)}
audit/types.go+0 −64
@@ -6,61 +6,12 @@ package auditimport ("context""io"-"time"-"github.com/hashicorp/go-bexpr""github.com/hashicorp/vault/internal/observability/event""github.com/hashicorp/vault/sdk/helper/salt""github.com/hashicorp/vault/sdk/logical")-// Audit subtypes.-const (-RequestType subtype = "AuditRequest"-ResponseType subtype = "AuditResponse"-)--// Audit formats.-const (-JSONFormat format = "json"-JSONxFormat format = "jsonx"-)--// version defines the version of audit events.-const version = "v0.1"--// subtype defines the type of audit event.-type subtype string--// format defines types of format audit events support.-type format string--// AuditEvent is the audit event.-type AuditEvent struct {-ID string `json:"id"`-Version string `json:"version"`-Subtype subtype `json:"subtype"` // the subtype of the audit event.-Timestamp time.Time `json:"timestamp"`-Data *logical.LogInput `json:"data"`-}--// Option is how options are passed as arguments.-type Option func(*options) error--// options are used to represent configuration for a audit related nodes.-type options struct {-withID string-withNow time.Time-withSubtype subtype-withFormat format-withPrefix string-withRaw bool-withElision bool-withOmitTime bool-withHMACAccessor bool-withHeaderFormatter HeaderFormatter-}-// Salter is an interface that provides a way to obtain a Salt for hashing.type Salter interface {// Salt returns a non-nil salt or an error.@@ -94,14 +45,6 @@ type HeaderFormatter interface {ApplyConfig(context.Context, map[string][]string, Salter) (map[string][]string, error)}-// EntryFormatter should be used to format audit requests and responses.-type EntryFormatter struct {-salter Salter-headerFormatter HeaderFormatter-config FormatterConfig-prefix string-}-// EntryFormatterWriter should be used to format and write out audit requests and responses.type EntryFormatterWriter struct {Formatter@@ -144,13 +87,6 @@ type FormatterConfig struct {RequiredFormat format}-// EntryFilter should be used to filter audit requests and responses which should-// make it to a sink.-type EntryFilter struct {-// the evaluator for the bexpr expression that should be applied by the node.-evaluator *bexpr.Evaluator-}-// RequestEntry is the structure of a request audit log entry.type RequestEntry struct {Time string `json:"time,omitempty"`<82990506+hc-github-team-secure-vault-core@users.noreply.github.com>290df86e6d50e8e5b7549093fe794400e4560816 (#25514)audit/types.go | 17 -----------------1 file changed, 17 deletions(-)
builtin/credential/cert/backend.go+43 −3
@@ -16,12 +16,19 @@ import ("github.com/hashicorp/go-hclog""github.com/hashicorp/go-multierror"+lru "github.com/hashicorp/golang-lru/v2""github.com/hashicorp/vault/sdk/framework""github.com/hashicorp/vault/sdk/helper/ocsp""github.com/hashicorp/vault/sdk/logical")-const operationPrefixCert = "cert"+const (+operationPrefixCert = "cert"+trustedCertPath = "cert/"++defaultRoleCacheSize = 200+maxRoleCacheSize = 10000+)func Factory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) {b := Backend()@@ -32,7 +39,11 @@ func Factory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend,}func Backend() *backend {-var b backend+// ignoring the error as it only can occur with <= 0 size+cache, _ := lru.New[string, *trusted](defaultRoleCacheSize)+b := backend{+trustedCache: cache,+}b.Backend = &framework.Backend{Help: backendHelp,PathsSpecial: &logical.Paths{@@ -59,6 +70,13 @@ func Backend() *backend {return &b}+type trusted struct {+pool *x509.CertPool+trusted []*ParsedCert+trustedNonCAs []*ParsedCert+ocspConf *ocsp.VerifyConfig+}+type backend struct {*framework.BackendMapCertId *framework.PathMap@@ -68,6 +86,9 @@ type backend struct {ocspClientMutex sync.RWMutexocspClient *ocsp.ClientconfigUpdated atomic.Bool++trustedCache *lru.Cache[string, *trusted]+trustedCacheDisabled atomic.Bool}func (b *backend) initialize(ctx context.Context, req *logical.InitializationRequest) error {@@ -98,6 +119,7 @@ func (b *backend) invalidate(_ context.Context, key string) {case key == "config":b.configUpdated.Store(true)}+b.flushTrustedCache()}func (b *backend) initOCSPClient(cacheSize int) {@@ -109,9 +131,21 @@ func (b *backend) initOCSPClient(cacheSize int) {func (b *backend) updatedConfig(config *config) {b.ocspClientMutex.Lock()defer b.ocspClientMutex.Unlock()++switch {+case config.RoleCacheSize < 0:+// Just to clean up memory+b.trustedCacheDisabled.Store(true)+b.trustedCache.Purge()+case config.RoleCacheSize == 0:+config.RoleCacheSize = defaultRoleCacheSize+fallthrough+default:+b.trustedCache.Resize(config.RoleCacheSize)+b.trustedCacheDisabled.Store(false)+}b.initOCSPClient(config.OcspCacheSize)b.configUpdated.Store(false)-return}func (b *backend) fetchCRL(ctx context.Context, storage logical.Storage, name string, crl *CRLInfo) error {@@ -161,6 +195,12 @@ func (b *backend) storeConfig(ctx context.Context, storage logical.Storage, confreturn nil}+func (b *backend) flushTrustedCache() {+if b.trustedCache != nil { // defensive+b.trustedCache.Purge()+}+}+const backendHelp = `The "cert" credential provider allows authentication usingTLS client certificates. A client connects to Vault and uses
builtin/credential/cert/path_login_test.go+17 −0
@@ -94,6 +94,10 @@ func TestCert_RoleResolve(t *testing.T) {testAccStepCert(t, "web", ca, "foo", allowed{dns: "example.com"}, false),testAccStepLoginWithName(t, connState, "web"),testAccStepResolveRoleWithName(t, connState, "web"),+// Test with caching disabled+testAccStepSetRoleCacheSize(t, -1),+testAccStepLoginWithName(t, connState, "web"),+testAccStepResolveRoleWithName(t, connState, "web"),},})}@@ -151,10 +155,23 @@ func TestCert_RoleResolveWithoutProvidingCertName(t *testing.T) {testAccStepCert(t, "web", ca, "foo", allowed{dns: "example.com"}, false),testAccStepLoginWithName(t, connState, "web"),testAccStepResolveRoleWithEmptyDataMap(t, connState, "web"),+testAccStepSetRoleCacheSize(t, -1),+testAccStepLoginWithName(t, connState, "web"),+testAccStepResolveRoleWithEmptyDataMap(t, connState, "web"),},})}+func testAccStepSetRoleCacheSize(t *testing.T, size int) logicaltest.TestStep {+return logicaltest.TestStep{+Operation: logical.UpdateOperation,+Path: "config",+Data: map[string]interface{}{+"role_cache_size": size,+},+}+}+func testAccStepResolveRoleWithEmptyDataMap(t *testing.T, connState tls.ConnectionState, certName string) logicaltest.TestStep {return logicaltest.TestStep{Operation: logical.ResolveRoleOperation,
ui/app/templates/vault/cluster/access/methods.hbs+34 −29
@@ -69,36 +69,41 @@</div><div class="level-right is-flex is-paddingless is-marginless"><div class="level-item">-<PopupMenu @name="auth-backend-nav">-<nav class="menu" aria-label="navigation for managing access method {{method.id}}">-<ul class="menu-list">-<li>-<LinkTo @route="vault.cluster.access.method.section" @models={{array method.id "configuration"}}>-View configuration-</LinkTo>-</li>-{{#if method.canEdit}}-<li>-<LinkTo @route="vault.cluster.settings.auth.configure" @model={{method.id}}>-Edit configuration-</LinkTo>-</li>-{{/if}}--{{#if (and (not-eq method.methodType "token") method.canDisable)}}-<ConfirmAction-@isInDropdown={{true}}-@confirmTitle="Disable method?"-@confirmMessage="This may affect access to Vault data."-@buttonText="Disable"-@onConfirmAction={{perform this.disableMethod method}}-/>-{{/if}}-</ul>-</nav>-</PopupMenu>+<Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|>+<dd.ToggleIcon+@icon="more-horizontal"+@text="Overflow options"+@hasChevron={{false}}+data-test-popup-menu-trigger+/>+<dd.Interactive+@text="View configuration"+@route="vault.cluster.access.method.section"+@models={{array method.id "configuration"}}+/>+{{#if (or method.canEdit (and (eq method.methodType "aws") method.canEditAws))}}+<dd.Interactive+@text="Edit configuration"+@route="vault.cluster.settings.auth.configure"+@model={{method.id}}+/>+{{/if}}+{{#if (and (not-eq method.methodType "token") method.canDisable)}}+<dd.Interactive @text="Disable" @color="critical" {{on "click" (fn (mut this.methodToDisable) method)}} />+{{/if}}+</Hds::Dropdown></div></div></div></LinkedBlock>-{{/each}}+{{/each}}++{{#if this.methodToDisable}}+<ConfirmModal+@color="critical"+@confirmTitle="Disable method?"+@confirmMessage="This may affect access to Vault data."+@onClose={{fn (mut this.methodToDisable) null}}+@onConfirm={{perform this.disableMethod this.methodToDisable}}+/>+{{/if}}