Security context
High· 7.5GHSA-2qmw-pvf7-4mw6 CVE-2024-6468CWE-703Published Jul 11, 2024

Hashicorp Vault vulnerable to Improper Check or Handling of Exceptional Conditions

Research this vulnerability

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

1.10.0 → fixed in 1.15.121.16.0-rc1 → fixed in 1.16.31.17.0-rc1 → fixed in 1.17.2

Details

Vault and Vault Enterprise did not properly handle requests originating from unauthorized IP addresses when the TCP listener option, proxy_protocol_behavior, was set to deny_unauthorized. When receiving a request from a source IP address that was not listed in proxy_protocol_authorized_addrs, the Vault API server would shut down and no longer respond to any HTTP requests, potentially resulting in denial of service. While this bug also affected versions of Vault up to 1.17.1 and 1.16.5, a separate regression in those release series did not allow Vault operators to configure the deny_unauthorized option, thus not allowing the conditions for the denial of service to occur. Fixed in Vault and Vault Enterprise 1.17.2, 1.16.6, and 1.15.12

The fix

Release delta 1.16.0-rc1 → 1.16.3 (contains the fix)

· Feb 13, 2024, 10:03 PM+2483624compare
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>
website/content/docs/concepts/filtering/audit.mdx+165 0
@@ -0,0 +1,165 @@
+---
+layout: docs
+page_title: Audit Filtering
+description: |-
+ How to filter audit entries in Vault.
+---
+
+[filter syntax]: /vault/docs/concepts/filtering
+
+# Filtering audit entries
+
+This page describes how to use filters with Vault audit devices to fine tune which
+audit entries should be written to an audit log.
+
+Starting in Vault 1.16.0, audit devices can be enabled with a `filter` option, which
+is used to evaluate audit entries and determine if they should be written to a particular
+audit log. Please ensure you are familiar with the [filtering concept](/vault/docs/concepts/filtering) before
+attempting to configure an audit device with a filter.
+
+Audit devices with and without filtering configured will all function simultaneously
+in Vault. Using a filtered audit device does not restrict Operators from *only* using
+the filter and fallback type devices described on this page.
+
+<Note title="Advanced audit feature">
+ The use of filtering in Vault's audit system should be considered an advanced feature.
+ Whilst it has been designed to be simple and flexible, exclusively enabling filtered
+ devices without a fallback configured could result in some requests and responses that are not audited.
+</Note>
+
+Please see the [Vault security model](/vault/docs/internals/security) for further
+information about how all requests and responses to Vault are usually audited.
+
+
+## `filter` option
+
+All audit device types ([file](/vault/docs/audit/file), [socket](/vault/docs/audit/socket)
+and [syslog](/vault/docs/audit/syslog)) support the `filter` option at the time they
+are enabled. After successfully enabling a device with a filter, every audit entry
+that Vault sends to that audit device will be compared to the expression in the filter.
+Only audit entries that match the filter will be written to the device's audit log.
+
+### Valid `filter` properties
+
+Filters can **only** reference the following properties of an audit entry:
+
+* `mount_point` - Path to the mount (e.g. [auth method](/vault/docs/auth), [secret engine](/vault/docs/secrets)),
+including namespaces.
+* `mount_type` - Type of mount being interacted with.
+* `namespace` - [Namespace](/vault/docs/enterprise/namespaces) 'path' that the request is taking place within.
+* `operation` - [Operation](/vault/docs/glossary#operation) being performed.
+* `path` - Full path of the request.
+
+### Example filters
+
+The following are examples that could be supplied as a `filter` value.
+
+| Purpose | Filter |
+|----------------------------------------------------------------------------------------|-----------------------------------------|
+| Only persist audit entries for requests within `ns1` | `namespace == ns1` |
+| Only persist audit entries for requests that are **not** in the root namespace | `namespace != \"\"` |
+| Only persist audit entries that interact with `kv` engines | `mount_type == kv` |
+| Only persist audit entries that attempt to perform `read` operations | `operation == read` |
+| Only persist audit entries for requests that interact with `kv` within namespace `ns2` | `namespace == ns2 and mount_type == kv` |
+
+### Scenario: `kv` only
+
+A Vault Operator wants only `kv` type audit entries to be written to an audit log for a particular device.
+
+1. Enable an audit device with the following filter option:
+
+ ```
+ vault audit enable -path kv-only file file_path=/var/audit.log filter="mount_type == kv"
+ ```
+
+2. List the enabled audit devices:
+
+ ```
+ vault audit list --detailed
+ ```
+
+3. Enable a `kv` secrets engine:
+
+ ```
+ vault secrets enable -path my-kv kv-v2
+ ```
+
+4. Write secret data to the `kv` engine:
+
+ ```
+ vault kv put my-kv foo=bar
+ ```
+
+5. Disable the audit device:
+
+ ```
+ vault audit disable -path kv-only
+ ```
+
+The steps above performed a number of actions against Vault that generate audit entries.
+However, only two of them had a `mount_type` of `kv` (steps 3 and 4).
+
+Viewing the contents of the file audit log at `/var/audit.log` will show that only audit
+entries related to steps 3 and 4, with a request and response audit entry for each.
+
+### Test message
+
+When enabling an audit device, Vault will (by default) attempt to send a test message
+to the device. In cases where filtering is configured on a device, it may be possible
+for the evaluation of the filter to result in the test message failing to meet the specified
+predicate expression, and therefore not being written to the audit device's sink. This test
+message will always be present in `fallback` devices (described below).
+
+The properties of the test message are as follows:
+
+* `mount_point` - not present
+* `mount_type` - not present.
+* `namespace` - not present.
+* `operation` - `update`.
+* `path` - `sys/audit/test`.
+
+Vault Operators should use this when trying to understand when they should or
+should not expect a test message to appear in the sink for a newly enabled audit device.
+
+## Fallback device
+
+The ability to filter audit entries can provide great flexibility to your workflows,
+however the additional complexity can make it possible for a Vault Operator to configure their
+audit devices in such a way that some audit entries are missed out from audit logs entirely.
+Therefore, we strongly encourage you to test out audit configurations in your non-production
+environments before deploying them to production.
+
+The `fallback` audit device is the (non-mandatory) mechanism by which Vault can
+continue to adhere to Vault's [security model](/vault/docs/internals/security),
+that all requests and responses are successfully logged before the client receives
+any secret material.
+
+When exclusively using filtered audit devices, enable the fallback audit device to
+catch any audit entries that would otherwise be missed.
+
+This means Vault is able to provide the same guarantee that 'at least one device must
+successfully write an audit entry' as when only standard/non-filtered audit devices
+are enabled.
+
+<Note title="Single fallback device">
+ Vault supports enabling only a single fallback audit device.
+</Note>
+
+#### Enabling the fallback audit device
+
+[Enabling the fallback audit device](/vault/docs/audit#fallback) requires supplying the `fallback` option:
+
+```shell-session
+vault audit enable -path=my-fallback -description="fallback device" file file_path=/tmp/audit.fallback.log fallback=true
+```
+
+### Metrics
+
+When the fallback device is enabled, and required to persist an audit entry to the audit log,
+Vault will emit a [fallback 'success' metric](/vault/docs/internals/telemetry/metrics/audit#vault-audit-fallback-success) on
+a successful write to the audit log.
+
+If audit devices are enabled that make use of filtering, but no fallback audit device
+has been enabled, Vault will produce a [fallback 'miss' metric](/vault/docs/internals/telemetry/metrics/audit#vault-audit-fallback-miss)
+as a way to allow Operators to understand how many auditable Vault entries are not
+being persisted to their audit logs.
ui/tests/acceptance/config-ui/messages/messages-test.js+39 0
@@ -12,6 +12,7 @@ import logout from 'vault/tests/pages/logout';
import { format, addDays, startOfDay } from 'date-fns';
import { datetimeLocalStringFormat } from 'core/utils/date-formatters';
import { PAGE } from 'vault/tests/helpers/config-ui/message-selectors';
+import { clickTrigger } from 'ember-power-select/test-support/helpers';
module('Acceptance | config-ui', function (hooks) {
setupApplicationTest(hooks);
@@ -93,6 +94,44 @@ module('Acceptance | config-ui', function (hooks) {
await click(PAGE.confirmButton);
assert.dom('[data-test-component="empty-state"]').exists('Message was deleted');
});
+ test('it should filter by type and status', async function (assert) {
+ assert.expect(6);
+ await this.createMessage('banner', null);
+ await this.createMessage('banner');
+ await visit('vault/config-ui/messages');
+
+ // check number of messages with status filters
+ await clickTrigger('#filter-by-message-status');
+ await click('.ember-power-select-options [data-option-index="0"]');
+ assert.dom('.linked-block').exists({ count: 1 }, 'filtered by active');
+ await click('[data-test-selected-list-button="delete"]');
+ await clickTrigger('#filter-by-message-status');
+ await click('.ember-power-select-options [data-option-index="1"]');
+ assert.dom('.linked-block').exists({ count: 1 }, 'filtered by inactive');
+ await click('[data-test-selected-list-button="delete"]');
+
+ // check number of messages with type filters
+ await clickTrigger('#filter-by-message-type');
+ await click('.ember-power-select-options [data-option-index="0"]');
+ assert.dom('.linked-block').exists({ count: 0 }, 'filtered by modal');
+ await click('[data-test-selected-list-button="delete"]');
+ await clickTrigger('#filter-by-message-type');
+ await click('.ember-power-select-options [data-option-index="1"]');
+ assert.dom('.linked-block').exists({ count: 2 }, 'filtered by banner');
+ await click('[data-test-selected-list-button="delete"]');
+
+ // check number of messages with no filters
+ assert.dom('.linked-block').exists({ count: 2 }, 'no filters selected');
+
+ // clean up custom messages
+ await click(PAGE.listItem('Awesome custom message title'));
+ await click(PAGE.confirmActionButton('Delete message'));
+ await click(PAGE.confirmButton);
+ await click(PAGE.listItem('Awesome custom message title'));
+ await click(PAGE.confirmActionButton('Delete message'));
+ await click(PAGE.confirmButton);
+ assert.dom('[data-test-component="empty-state"]').exists('Message was deleted');
+ });
test('it should display preview a message when all required fields are filled out', async function (assert) {
assert.expect(2);
await visit(`vault/config-ui/messages`);
(#25416)
.../vault/cluster/access/methods.js | 3 +
ui/app/models/auth-method.js | 105 +++++++++---------
.../vault/cluster/access/methods.hbs | 63 ++++++-----
ui/tests/acceptance/auth-list-test.js | 15 ++-
4 files changed, 101 insertions(+), 85 deletions(-)
helper/testhelpers/corehelpers/corehelpers.go+14 0
@@ -315,6 +315,10 @@ type noopWrapper struct {
backend *NoopAudit
}
+// NoopAuditEventListener is a callback used by noopWrapper.Process() to notify
+// of each received audit event.
+type NoopAuditEventListener func(*audit.AuditEvent)
+
type NoopAudit struct {
Config *audit.BackendConfig
@@ -339,6 +343,8 @@ type NoopAudit struct {
nodeIDList []eventlogger.NodeID
nodeMap map[eventlogger.NodeID]eventlogger.Node
+
+ listener NoopAuditEventListener
}
// Process handles the contortions required by older test code to ensure behavior.
@@ -358,6 +364,10 @@ func (n *noopWrapper) Process(ctx context.Context, e *eventlogger.Event) (*event
return nil, errors.New("cannot parse payload as an audit event")
}
+ if n.backend.listener != nil {
+ n.backend.listener(a)
+ }
+
in := a.Data
// Depending on the type of the audit event (request or response) we need to
@@ -495,6 +505,10 @@ func (n *NoopAudit) RegisterNodesAndPipeline(broker *eventlogger.Broker, name st
return broker.RegisterPipeline(pipeline)
}
+func (n *NoopAudit) SetListener(listener NoopAuditEventListener) {
+ n.listener = listener
+}
+
type TestLogger struct {
hclog.InterceptLogger
Path string
<82990506+hc-github-team-secure-vault-core@users.noreply.github.com>
808cdfc2d28afcb2c5285be1355cf31fd7bdc53c (#25512)
audit/entry_filter.go | 7 ++++
audit/entry_formatter.go | 8 +++++
audit/entry_formatter_test.go | 24 ++++++++++---
audit/event.go | 39 +++++++++++++++++++--
audit/event_test.go | 47 +++++++++++++++++++++++++
audit/nodes_test.go | 2 ++
audit/options.go | 17 ++++++++++
audit/options_test.go | 24 +++++++++++++
audit/types.go | 64 -----------------------------------
9 files changed, 160 insertions(+), 72 deletions(-)
website/content/api-docs/system/audit.mdx+19 12
@@ -17,11 +17,11 @@ enabled at a time.
This endpoint lists only the enabled audit devices (it does not list all
available audit devices).
-- **`sudo` required** – This endpoint requires `sudo` capability in addition to
+- **`sudo` required** – This endpoint requires `sudo` capability in addition to
any path-specific capabilities.
| Method | Path |
-| :----- | :----------- |
+|:-------|:-------------|
| `GET` | `/sys/audit` |
### Sample request
@@ -51,25 +51,28 @@ $ curl \
This endpoint enables a new audit device at the supplied path. The path can be a
single word name or a more complex, nested path.
-- **`sudo` required** – This endpoint requires `sudo` capability in addition to
+- **`sudo` required** – This endpoint requires `sudo` capability in addition to
any path-specific capabilities.
| Method | Path |
-| :----- | :----------------- |
+|:-------|:-------------------|
| `POST` | `/sys/audit/:path` |
### Parameters
-- `path` `(string: <required>)` – Specifies the path in which to enable the audit
+- `path` `(string: <required>)` – Specifies the path in which to enable the audit
device. This is part of the request URL.
-- `description` `(string: "")` – Specifies a human-friendly description of the
+- `description` `(string: "")` – Specifies a human-friendly description of the
audit device.
-- `options` `(map<string|string>: nil)` – Specifies configuration options to pass to the audit device itself.
- For more details, please see the relevant page for an audit device `type`, under [Audit Devices docs](/vault/docs/audit).
+- `options` `(map<string|string>: nil)` – Specifies configuration options to pass to the audit device itself.
+ There are a set of [common options](#common-configuration-options)
+ which can be applied to all types of audit device.
+ For more details, please see the relevant page for an audit device `type`,
+ under [Audit Devices docs](/vault/docs/audit).
-- `type` `(string: <required>)` – Specifies the type of the audit device.
+- `type` `(string: <required>)` – Specifies the type of the audit device.
Valid types are `file`, `socket` and `syslog`.
Additionally, the following options are allowed in Vault Community Edition, but
@@ -78,6 +81,10 @@ relevant functionality is only supported in Vault Enterprise:
- `local` `(bool: false)` – Applies exclusively to performance replication. Specifies if the audit device is local within the cluster only. Local
audit devices are not replicated nor (if a secondary) removed by replication.
+#### Common configuration options
+
+@include 'audit-options-common.mdx'
+
### Sample payload
```json
@@ -107,16 +114,16 @@ This endpoint disables the audit device at the given path.
for comparison with entries in the audit logs. This is true even if you re-enable
the audit device at the same path, as a new salt will be created for hashing.
-- **`sudo` required** – This endpoint requires `sudo` capability in addition to
+- **`sudo` required** – This endpoint requires `sudo` capability in addition to
any path-specific capabilities.
| Method | Path |
-| :------- | :----------------- |
+|:---------|:-------------------|
| `DELETE` | `/sys/audit/:path` |
### Parameters
-- `path` `(string: <required>)` – Specifies the path of the audit device to
+- `path` `(string: <required>)` – Specifies the path of the audit device to
delete. This is part of the request URL.
### Sample request
audit/types.go+0 64
@@ -6,61 +6,12 @@ package audit
import (
"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(-)
audit/options.go+17 0
@@ -10,6 +10,23 @@ import (
"time"
)
+// 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
+}
+
// getDefaultOptions returns options with their default values.
func getDefaultOptions() options {
return options{
ui/lib/config-ui/addon/components/messages/page/list.hbs+34 10
@@ -9,16 +9,40 @@
@breadcrumbs={{this.breadcrumbs}}
>
<:toolbarFilters>
- {{#if @messages.meta.total}}
- <FilterInput
- aria-label="Search by message title"
- placeholder="Search by message title"
- id="message-filter"
- value={{@pageFilter}}
- @autofocus={{true}}
- @onInput={{this.onFilterChange}}
+ <FilterInput
+ aria-label="Search by message title"
+ placeholder="Search by message title"
+ id="message-filter"
+ value={{@params.pageFilter}}
+ @autofocus={{true}}
+ @onInput={{this.onFilterInputChange}}
+ />
+ <div>
+ <SearchSelect
+ @id="filter-by-message-status"
+ class="has-left-margin-s"
+ @options={{this.statusFilterOptions}}
+ @selectLimit="1"
+ @searchEnabled={{false}}
+ @fallbackComponent="select"
+ @onChange={{fn this.onFilterChange "status"}}
+ @placeholder="Filter by message status"
+ @inputValue={{if @params.status (array @params.status)}}
+ data-test-filter-by-message-status
/>
- {{/if}}
+ </div>
+ <SearchSelect
+ @id="filter-by-message-type"
+ class="has-left-margin-s"
+ @options={{this.typeFilterOptions}}
+ @selectLimit="1"
+ @searchEnabled={{false}}
+ @fallbackComponent="select"
+ @onChange={{fn this.onFilterChange "type"}}
+ @placeholder="Filter by message type"
+ @inputValue={{if @params.type (array @params.type)}}
+ data-test-filter-by-message-type
+ />
</:toolbarFilters>
<:toolbarActions>
<Hds::Button
@@ -68,7 +92,7 @@
<dd.Interactive @text="Edit" @route="messages.message.edit" @model={{message.id}} />
{{/if}}
{{#if message.canDeleteCustomMessages}}
- <dd.Interactive @text="Disable" @color="critical" {{on "click" (fn (mut this.messageToDelete) message)}} />
+ <dd.Interactive @text="Delete" @color="critical" {{on "click" (fn (mut this.messageToDelete) message)}} />
{{/if}}
</Hds::Dropdown>
{{/if}}
ui/lib/config-ui/addon/components/messages/page/list.js+30 4
@@ -91,6 +91,20 @@ export default class MessagesList extends Component {
return [{ label: 'Messages' }, { label }];
}
+ get statusFilterOptions() {
+ return [
+ { id: 'active', name: 'active' },
+ { id: 'inactive', name: 'inactive' },
+ ];
+ }
+
+ get typeFilterOptions() {
+ return [
+ { id: 'modal', name: 'modal' },
+ { id: 'banner', name: 'banner' },
+ ];
+ }
+
// callback from HDS pagination to set the queryParams page
get paginationQueryParams() {
return (page) => {
@@ -100,6 +114,12 @@ export default class MessagesList extends Component {
};
}
+ transitionToMessagesWithParams(queryParams) {
+ this.router.transitionTo('vault.cluster.config-ui.messages', {
+ queryParams,
+ });
+ }
+
@task
*deleteMessage(message) {
try {
@@ -117,10 +137,16 @@ export default class MessagesList extends Component {
}
@action
- onFilterChange(pageFilter) {
- this.router.transitionTo('vault.cluster.config-ui.messages', {
- queryParams: { pageFilter },
- });
+ onFilterInputChange(pageFilter) {
+ this.transitionToMessagesWithParams({ pageFilter });
+ }
+
+ @action
+ onFilterChange(filterType, [filterOption]) {
+ const param = {};
+ param[filterType] = filterOption;
+ param.page = 1;
+ this.transitionToMessagesWithParams(param);
}
@action
website/content/docs/platform/k8s/vso/helm.mdx+29 3
@@ -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=dbdfa37e122bd91a4db0bb016970b717aca544f5 -->
+ the vault-secrets-operator repo's values.yaml: file commit=12d914f6ad83b28d56bc629cde6cbfd5ce1475f0 -->
<!-- codegen: start -->
## Top-Level Stanzas
@@ -79,6 +79,8 @@ Use these links to navigate to a particular top-level stanza.
- `image` ((#v-controller-kuberbacproxy-image)) - Image sets the repo and tag of the kube-rbac-proxy image to use for the controller.
+ - `pullPolicy` ((#v-controller-kuberbacproxy-image-pullpolicy)) (`string: IfNotPresent`)
+
- `repository` ((#v-controller-kuberbacproxy-image-repository)) (`string: gcr.io/kubebuilder/kube-rbac-proxy`)
- `tag` ((#v-controller-kuberbacproxy-image-tag)) (`string: v0.15.0`)
@@ -117,9 +119,16 @@ Use these links to navigate to a particular top-level stanza.
- `image` ((#v-controller-manager-image)) - Image sets the repo and tag of the vault-secrets-operator image to use for the controller.
+ - `pullPolicy` ((#v-controller-manager-image-pullpolicy)) (`string: IfNotPresent`)
+
- `repository` ((#v-controller-manager-image-repository)) (`string: hashicorp/vault-secrets-operator`)
- - `tag` ((#v-controller-manager-image-tag)) (`string: 0.4.3`)
+ - `tag` ((#v-controller-manager-image-tag)) (`string: 0.5.0`)
+
+ - `globalTransformationOptions` ((#v-controller-manager-globaltransformationoptions)) (`array<string>: ""`) - Global secret transformation options.
+
+ - `excludeRaw` ((#v-controller-manager-globaltransformationoptions-excluderaw)) (`boolean: false`) - excludeRaw directs the operator to prevent _raw secret data being stored
+ in the destination K8s Secret.
- `clientCache` ((#v-controller-manager-clientcache)) - Configures the client cache which is used by the controller to cache (and potentially persist) vault tokens that
are the result of using the VaultAuthMethod. This enables re-use of Vault Tokens
@@ -127,6 +136,7 @@ Use these links to navigate to a particular top-level stanza.
Persistence is only useful in the context of Dynamic Secrets, so "none" is an okay default.
- `persistenceModel` ((#v-controller-manager-clientcache-persistencemodel)) (`string: ""`) - Defines the `-client-cache-persistence-model` which caches+persists vault tokens.
+ May also be set via the `VSO_CLIENT_CACHE_PERSISTENCE_MODEL` environment variable.
Valid values are:
"none" - in-memory client cache is used, no tokens are persisted.
"direct-unencrypted" - in-memory client cache is persisted, unencrypted. This is NOT recommended for any production workload.
@@ -138,6 +148,7 @@ Use these links to navigate to a particular top-level stanza.
default: "none"
- `cacheSize` ((#v-controller-manager-clientcache-cachesize)) (`integer: ""`) - Defines the size of the in-memory LRU cache *in entries*, that is used by the client cache controller.
+ May also be set via the `VSO_CLIENT_CACHE_SIZE` environment variable.
Larger numbers will increase memory usage by the controller, lower numbers will cause more frequent evictions
of the client cache which can result in additional Vault client counts.
@@ -263,14 +274,27 @@ Use these links to navigate to a particular top-level stanza.
X-vault-something1: "foo"
- `maxConcurrentReconciles` ((#v-controller-manager-maxconcurrentreconciles)) (`integer: ""`) - Defines the maximum number of concurrent reconciles for each controller.
+ May also be set via the `VSO_MAX_CONCURRENT_RECONCILES` environment variable.
default: 100
- `extraEnv` ((#v-controller-manager-extraenv)) (`array<map>`) - Defines additional environment variables to be added to the
- vault-secrets-opearator manager container.
+ vault-secrets-operator manager container.
+ Example:
+
+ ```yaml
extraEnv:
- name: HTTP_PROXY
value: http://proxy.example.com
+ - name: VSO_OUTPUT_FORMAT
+ value: json
+ - name: VSO_CLIENT_CACHE_SIZE
+ value: "20000"
+ - name: VSO_CLIENT_CACHE_PERSISTENCE_MODEL
+ value: "direct-encrypted"
+ - name: VSO_MAX_CONCURRENT_RECONCILES
+ value: "30"
+ ```
- `extraArgs` ((#v-controller-manager-extraargs)) (`array: []`) - Defines additional commandline arguments to be passed to the
vault-secrets-operator manager container.
@@ -390,6 +414,8 @@ Use these links to navigate to a particular top-level stanza.
- `namespace` ((#v-defaultauthmethod-namespace)) (`string: ""`) - Vault namespace for the VaultAuthMethod CR
+ - `allowedNamespaces` ((#v-defaultauthmethod-allowednamespaces)) (`array<string>: []`) - Kubernetes namespace glob patterns which are allow-listed for use with the default AuthMethod.
+
- `method` ((#v-defaultauthmethod-method)) (`string: kubernetes`) - Vault Auth method to be used with the VaultAuthMethod CR
- `mount` ((#v-defaultauthmethod-mount)) (`string: kubernetes`) - Mount path for the Vault Auth Method.
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.
command/agent_test.go+155 0
@@ -5,9 +5,11 @@ package command
import (
"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.
website/content/docs/commands/audit/enable.mdx+8 3
@@ -23,9 +23,6 @@ $ vault audit enable file file_path=/tmp/my-file.txt
Success! Enabled the file audit device at: file/
```
-Full configuration parameters for each audit device are available on the
-[Audit Devices](/vault/docs/audit) page.
-
## Usage
The following flags are available in addition to the [standard set of
@@ -40,3 +37,11 @@ flags](/vault/docs/commands) included on all commands.
- `-path` `(string: "")` - Place where the audit device will be accessible. This
must be unique across all audit devices. This defaults to the "type" of the
audit device.
+
+All audit devices support the following common options which can be supplied after
+the flags documented above:
+
+@include 'audit-options-common.mdx'
+
+For device specific options, refer to the relevant audit device type under the [Audit Devices](/vault/docs/audit)
+overview.
website/content/docs/glossary.mdx+28 0
@@ -16,6 +16,7 @@ documentation for Vault.
- [Barrier](#barrier)
- [Client Token](#client-token)
- [Plugin](#plugin)
+- [Request](#request)
- [Secret](#secret)
- [Secrets Engine](#secrets-engine)
- [Server](#server)
@@ -76,6 +77,33 @@ An external plugin may make use of [plugin multiplexing](/vault/docs/plugins/plu
A multiplexed plugin allows a single plugin process to be used for multiple
mounts of the same type.
+### Request
+
+A request being made to Vault contains all relevant parameters and context in order
+for Vault to be able to act accordingly. Vault represents this request internally
+in a way that understands:
+
+* Mount Point - Used to generate relative paths.
+* Mount Type - The type of mount the request is interacting with.
+* Namespace - The [namespace](/vault/docs/enterprise/namespaces) the request is taking place within.
+* Operation - See [the operation description](#operation) below for the supported operations.
+* Path - The full path of the request.
+
+<Note title="Request's Namespace">
+ The Namespace a request is targeting may be specified either as part of the path
+ or the Vault Namespace header.
+</Note>
+
+Please see our Enterprise documentation for further information on how
+[Namespaces can be specified](/vault/docs/enterprise/namespaces#vault-api-and-namespaces)
+as part of a request.
+
+#### Operation
+
+The request's operation can be one of the following: `alias-lookahead`, `create`, `delete`,
+`header`, `help`, `list`, `patch`, `read`, `renew`, `resolve-role`, `revoke`, `rollback`
+`update`.
+
### Secret
A secret is the term for anything returned by Vault which
ui/lib/core/addon/components/certificate-card.js+20 9
@@ -17,24 +17,35 @@ import Component from '@glimmer/component';
* <CertificateCard @data={{value}} @isPem={{true}} />
* ```
* @param {string} data - the data to be displayed in the component (usually in PEM or DER format)
- * @param {boolean} isPem - optional argument for if the data is required to be in PEM format (and should thus have the PEM Format label)
+ * @param {boolean} [isPem] - optional argument for if the data is required to be in PEM format (and should thus have the PEM Format label)
*/
export default class CertificateCardComponent extends Component {
- // Returns the format the data is in: PEM, DER, or no format if no data is provided
- get format() {
+ get certLabel() {
if (!this.args.data) return '';
- let value;
- if (typeof this.args.data === 'object') {
- value = this.args.data[0];
- } else {
- value = this.args.data;
- }
+ const value = Array.isArray(this.args.data) ? this.args.data[0] : this.args.data;
if (value.substring(0, 11) === '-----BEGIN ' || this.args.isPem === true) {
return 'PEM Format';
}
return 'DER Format';
}
+
+ get copyValue() {
+ const { data } = this.args;
+ if (!data) return data;
+ const type = Array.isArray(data) ? 'array' : typeof data;
+ switch (type) {
+ case 'string':
+ return data;
+ case 'array':
+ return data.join('\n');
+ case 'object':
+ // unlikely for certificates but just in case
+ return JSON.stringify(data);
+ default:
+ return data.toString();
+ }
+ }
}
More files changed — see the full commit.

Release delta 1.17.0-rc1 → 1.17.2 (contains the fix)

· May 29, 2024, 03:24 PM+2856665compare
sdk/plugin/pb/backend_grpc.pb.go+77 30
@@ -3,7 +3,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
-// - protoc-gen-go-grpc v1.3.0
+// - protoc-gen-go-grpc v1.4.0
// - protoc (unknown)
// source: sdk/plugin/pb/backend.proto
@@ -18,8 +18,8 @@ import (
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
-// Requires gRPC-Go v1.32.0 or later.
-const _ = grpc.SupportPackageIsVersion7
+// Requires gRPC-Go v1.62.0 or later.
+const _ = grpc.SupportPackageIsVersion8
const (
Backend_HandleRequest_FullMethodName = "/pb.Backend/HandleRequest"
@@ -35,6 +35,10 @@ const (
// BackendClient is the client API for Backend service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+//
+// Backend is the interface that plugins must satisfy. The plugin should
+// implement the server for this service. Requests will first run the
+// HandleExistenceCheck rpc then run the HandleRequests rpc.
type BackendClient interface {
// HandleRequest is used to handle a request and generate a response.
// The plugins must check the operation type and handle appropriately.
@@ -82,8 +86,9 @@ func NewBackendClient(cc grpc.ClientConnInterface) BackendClient {
}
func (c *backendClient) HandleRequest(ctx context.Context, in *HandleRequestArgs, opts ...grpc.CallOption) (*HandleRequestReply, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(HandleRequestReply)
- err := c.cc.Invoke(ctx, Backend_HandleRequest_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Backend_HandleRequest_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -91,8 +96,9 @@ func (c *backendClient) HandleRequest(ctx context.Context, in *HandleRequestArgs
}
func (c *backendClient) SpecialPaths(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*SpecialPathsReply, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(SpecialPathsReply)
- err := c.cc.Invoke(ctx, Backend_SpecialPaths_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Backend_SpecialPaths_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -100,8 +106,9 @@ func (c *backendClient) SpecialPaths(ctx context.Context, in *Empty, opts ...grp
}
func (c *backendClient) HandleExistenceCheck(ctx context.Context, in *HandleExistenceCheckArgs, opts ...grpc.CallOption) (*HandleExistenceCheckReply, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(HandleExistenceCheckReply)
- err := c.cc.Invoke(ctx, Backend_HandleExistenceCheck_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Backend_HandleExistenceCheck_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -109,8 +116,9 @@ func (c *backendClient) HandleExistenceCheck(ctx context.Context, in *HandleExis
}
func (c *backendClient) Cleanup(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(Empty)
- err := c.cc.Invoke(ctx, Backend_Cleanup_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Backend_Cleanup_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -118,8 +126,9 @@ func (c *backendClient) Cleanup(ctx context.Context, in *Empty, opts ...grpc.Cal
}
func (c *backendClient) InvalidateKey(ctx context.Context, in *InvalidateKeyArgs, opts ...grpc.CallOption) (*Empty, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(Empty)
- err := c.cc.Invoke(ctx, Backend_InvalidateKey_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Backend_InvalidateKey_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -127,8 +136,9 @@ func (c *backendClient) InvalidateKey(ctx context.Context, in *InvalidateKeyArgs
}
func (c *backendClient) Setup(ctx context.Context, in *SetupArgs, opts ...grpc.CallOption) (*SetupReply, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(SetupReply)
- err := c.cc.Invoke(ctx, Backend_Setup_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Backend_Setup_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -136,8 +146,9 @@ func (c *backendClient) Setup(ctx context.Context, in *SetupArgs, opts ...grpc.C
}
func (c *backendClient) Initialize(ctx context.Context, in *InitializeArgs, opts ...grpc.CallOption) (*InitializeReply, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(InitializeReply)
- err := c.cc.Invoke(ctx, Backend_Initialize_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Backend_Initialize_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -145,8 +156,9 @@ func (c *backendClient) Initialize(ctx context.Context, in *InitializeArgs, opts
}
func (c *backendClient) Type(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*TypeReply, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(TypeReply)
- err := c.cc.Invoke(ctx, Backend_Type_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Backend_Type_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -156,6 +168,10 @@ func (c *backendClient) Type(ctx context.Context, in *Empty, opts ...grpc.CallOp
// BackendServer is the server API for Backend service.
// All implementations must embed UnimplementedBackendServer
// for forward compatibility
+//
+// Backend is the interface that plugins must satisfy. The plugin should
+// implement the server for this service. Requests will first run the
+// HandleExistenceCheck rpc then run the HandleRequests rpc.
type BackendServer interface {
// HandleRequest is used to handle a request and generate a response.
// The plugins must check the operation type and handle appropriately.
@@ -434,6 +450,9 @@ const (
// StorageClient is the client API for Storage service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+//
+// Storage is the way that plugins are able read/write data. Plugins should
+// implement the client for this service.
type StorageClient interface {
List(ctx context.Context, in *StorageListArgs, opts ...grpc.CallOption) (*StorageListReply, error)
Get(ctx context.Context, in *StorageGetArgs, opts ...grpc.CallOption) (*StorageGetReply, error)
@@ -450,8 +469,9 @@ func NewStorageClient(cc grpc.ClientConnInterface) StorageClient {
}
func (c *storageClient) List(ctx context.Context, in *StorageListArgs, opts ...grpc.CallOption) (*StorageListReply, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(StorageListReply)
- err := c.cc.Invoke(ctx, Storage_List_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Storage_List_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -459,8 +479,9 @@ func (c *storageClient) List(ctx context.Context, in *StorageListArgs, opts ...g
}
func (c *storageClient) Get(ctx context.Context, in *StorageGetArgs, opts ...grpc.CallOption) (*StorageGetReply, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(StorageGetReply)
- err := c.cc.Invoke(ctx, Storage_Get_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Storage_Get_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -468,8 +489,9 @@ func (c *storageClient) Get(ctx context.Context, in *StorageGetArgs, opts ...grp
}
func (c *storageClient) Put(ctx context.Context, in *StoragePutArgs, opts ...grpc.CallOption) (*StoragePutReply, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(StoragePutReply)
- err := c.cc.Invoke(ctx, Storage_Put_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Storage_Put_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -477,8 +499,9 @@ func (c *storageClient) Put(ctx context.Context, in *StoragePutArgs, opts ...grp
}
func (c *storageClient) Delete(ctx context.Context, in *StorageDeleteArgs, opts ...grpc.CallOption) (*StorageDeleteReply, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(StorageDeleteReply)
- err := c.cc.Invoke(ctx, Storage_Delete_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Storage_Delete_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -488,6 +511,9 @@ func (c *storageClient) Delete(ctx context.Context, in *StorageDeleteArgs, opts
// StorageServer is the server API for Storage service.
// All implementations must embed UnimplementedStorageServer
// for forward compatibility
+//
+// Storage is the way that plugins are able read/write data. Plugins should
+// implement the client for this service.
type StorageServer interface {
List(context.Context, *StorageListArgs) (*StorageListReply, error)
Get(context.Context, *StorageGetArgs) (*StorageGetReply, error)
@@ -645,6 +671,9 @@ const (
// SystemViewClient is the client API for SystemView service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+//
+// SystemView exposes system configuration information in a safe way for plugins
+// to consume. Plugins should implement the client for this service.
type SystemViewClient interface {
// DefaultLeaseTTL returns the default lease TTL set in Vault configuration
DefaultLeaseTTL(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*TTLReply, error)
@@ -699,8 +728,9 @@ func NewSystemViewClient(cc grpc.ClientConnInterface) SystemViewClient {
}
func (c *systemViewClient) DefaultLeaseTTL(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*TTLReply, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(TTLReply)
- err := c.cc.Invoke(ctx, SystemView_DefaultLeaseTTL_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, SystemView_DefaultLeaseTTL_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -708,8 +738,9 @@ func (c *systemViewClient) DefaultLeaseTTL(ctx context.Context, in *Empty, opts
}
func (c *systemViewClient) MaxLeaseTTL(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*TTLReply, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(TTLReply)
- err := c.cc.Invoke(ctx, SystemView_MaxLeaseTTL_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, SystemView_MaxLeaseTTL_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -717,8 +748,9 @@ func (c *systemViewClient) MaxLeaseTTL(ctx context.Context, in *Empty, opts ...g
}
func (c *systemViewClient) Tainted(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*TaintedReply, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(TaintedReply)
- err := c.cc.Invoke(ctx, SystemView_Tainted_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, SystemView_Tainted_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -726,8 +758,9 @@ func (c *systemViewClient) Tainted(ctx context.Context, in *Empty, opts ...grpc.
}
func (c *systemViewClient) CachingDisabled(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*CachingDisabledReply, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(CachingDisabledReply)
- err := c.cc.Invoke(ctx, SystemView_CachingDisabled_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, SystemView_CachingDisabled_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -735,8 +768,9 @@ func (c *systemViewClient) CachingDisabled(ctx context.Context, in *Empty, opts
}
func (c *systemViewClient) ReplicationState(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*ReplicationStateReply, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ReplicationStateReply)
- err := c.cc.Invoke(ctx, SystemView_ReplicationState_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, SystemView_ReplicationState_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -744,8 +778,9 @@ func (c *systemViewClient) ReplicationState(ctx context.Context, in *Empty, opts
}
func (c *systemViewClient) ResponseWrapData(ctx context.Context, in *ResponseWrapDataArgs, opts ...grpc.CallOption) (*ResponseWrapDataReply, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ResponseWrapDataReply)
- err := c.cc.Invoke(ctx, SystemView_ResponseWrapData_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, SystemView_ResponseWrapData_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -753,8 +788,9 @@ func (c *systemViewClient) ResponseWrapData(ctx context.Context, in *ResponseWra
}
func (c *systemViewClient) MlockEnabled(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*MlockEnabledReply, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MlockEnabledReply)
- err := c.cc.Invoke(ctx, SystemView_MlockEnabled_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, SystemView_MlockEnabled_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -762,8 +798,9 @@ func (c *systemViewClient) MlockEnabled(ctx context.Context, in *Empty, opts ...
}
func (c *systemViewClient) LocalMount(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*LocalMountReply, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(LocalMountReply)
- err := c.cc.Invoke(ctx, SystemView_LocalMount_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, SystemView_LocalMount_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -771,8 +808,9 @@ func (c *systemViewClient) LocalMount(ctx context.Context, in *Empty, opts ...gr
}
func (c *systemViewClient) EntityInfo(ctx context.Context, in *EntityInfoArgs, opts ...grpc.CallOption) (*EntityInfoReply, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(EntityInfoReply)
- err := c.cc.Invoke(ctx, SystemView_EntityInfo_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, SystemView_EntityInfo_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -780,8 +818,9 @@ func (c *systemViewClient) EntityInfo(ctx context.Context, in *EntityInfoArgs, o
}
func (c *systemViewClient) PluginEnv(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*PluginEnvReply, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(PluginEnvReply)
- err := c.cc.Invoke(ctx, SystemView_PluginEnv_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, SystemView_PluginEnv_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -789,8 +828,9 @@ func (c *systemViewClient) PluginEnv(ctx context.Context, in *Empty, opts ...grp
}
func (c *systemViewClient) GroupsForEntity(ctx context.Context, in *EntityInfoArgs, opts ...grpc.CallOption) (*GroupsForEntityReply, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GroupsForEntityReply)
- err := c.cc.Invoke(ctx, SystemView_GroupsForEntity_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, SystemView_GroupsForEntity_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -798,8 +838,9 @@ func (c *systemViewClient) GroupsForEntity(ctx context.Context, in *EntityInfoAr
}
func (c *systemViewClient) GeneratePasswordFromPolicy(ctx context.Context, in *GeneratePasswordFromPolicyRequest, opts ...grpc.CallOption) (*GeneratePasswordFromPolicyReply, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GeneratePasswordFromPolicyReply)
- err := c.cc.Invoke(ctx, SystemView_GeneratePasswordFromPolicy_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, SystemView_GeneratePasswordFromPolicy_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -807,8 +848,9 @@ func (c *systemViewClient) GeneratePasswordFromPolicy(ctx context.Context, in *G
}
func (c *systemViewClient) ClusterInfo(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*ClusterInfoReply, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ClusterInfoReply)
- err := c.cc.Invoke(ctx, SystemView_ClusterInfo_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, SystemView_ClusterInfo_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -816,8 +858,9 @@ func (c *systemViewClient) ClusterInfo(ctx context.Context, in *Empty, opts ...g
}
func (c *systemViewClient) GenerateIdentityToken(ctx context.Context, in *GenerateIdentityTokenRequest, opts ...grpc.CallOption) (*GenerateIdentityTokenResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GenerateIdentityTokenResponse)
- err := c.cc.Invoke(ctx, SystemView_GenerateIdentityToken_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, SystemView_GenerateIdentityToken_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -827,6 +870,9 @@ func (c *systemViewClient) GenerateIdentityToken(ctx context.Context, in *Genera
// SystemViewServer is the server API for SystemView service.
// All implementations must embed UnimplementedSystemViewServer
// for forward compatibility
+//
+// SystemView exposes system configuration information in a safe way for plugins
+// to consume. Plugins should implement the client for this service.
type SystemViewServer interface {
// DefaultLeaseTTL returns the default lease TTL set in Vault configuration
DefaultLeaseTTL(context.Context, *Empty) (*TTLReply, error)
@@ -1272,8 +1318,9 @@ func NewEventsClient(cc grpc.ClientConnInterface) EventsClient {
}
func (c *eventsClient) SendEvent(ctx context.Context, in *SendEventRequest, opts ...grpc.CallOption) (*Empty, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(Empty)
- err := c.cc.Invoke(ctx, Events_SendEvent_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Events_SendEvent_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
ui/tests/integration/components/kubernetes/tab-page-header-test.js+26 12
@@ -9,6 +9,8 @@ import { setupEngine } from 'ember-engines/test-support';
import { setupMirage } from 'ember-cli-mirage/test-support';
import { render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
+import { GENERAL } from 'vault/tests/helpers/general-selectors';
+import sinon from 'sinon';
module('Integration | Component | kubernetes | TabPageHeader', function (hooks) {
setupRenderingTest(hooks);
@@ -28,12 +30,18 @@ module('Integration | Component | kubernetes | TabPageHeader', function (hooks)
this.model = this.store.peekRecord('secret-engine', 'kubernetes-test');
this.mount = this.model.path.slice(0, -1);
this.breadcrumbs = [{ label: 'Secrets', route: 'secrets', linkExternal: true }, { label: this.mount }];
+ this.handleSearch = sinon.spy();
+ this.handleInput = sinon.spy();
+ this.handleKeyDown = sinon.spy();
});
test('it should render breadcrumbs', async function (assert) {
- await render(hbs`<TabPageHeader @model={{this.model}} @breadcrumbs={{this.breadcrumbs}} />`, {
- owner: this.engine,
- });
+ await render(
+ hbs`<TabPageHeader @model={{this.model}} @breadcrumbs={{this.breadcrumbs}} @handleSearch={{this.handleSearch}} @handleInput={{this.handleInput}} @handleKeyDown={{this.handleKeyDown}} />`,
+ {
+ owner: this.engine,
+ }
+ );
assert.dom('[data-test-breadcrumbs] li:nth-child(1) a').hasText('Secrets', 'Secrets breadcrumb renders');
assert
@@ -42,9 +50,12 @@ module('Integration | Component | kubernetes | TabPageHeader', function (hooks)
});
test('it should render title', async function (assert) {
- await render(hbs`<TabPageHeader @model={{this.model}} @breadcrumbs={{this.breadcrumbs}} />`, {
- owner: this.engine,
- });
+ await render(
+ hbs`<TabPageHeader @model={{this.model}} @breadcrumbs={{this.breadcrumbs}} @handleSearch={{this.handleSearch}} @handleInput={{this.handleInput}} @handleKeyDown={{this.handleKeyDown}} />`,
+ {
+ owner: this.engine,
+ }
+ );
assert
.dom('[data-test-header-title] svg')
.hasClass('flight-icon-kubernetes-color', 'Correct icon renders in title');
@@ -52,9 +63,12 @@ module('Integration | Component | kubernetes | TabPageHeader', function (hooks)
});
test('it should render tabs', async function (assert) {
- await render(hbs`<TabPageHeader @model={{this.model}} @breadcrumbs={{this.breadcrumbs}} />`, {
- owner: this.engine,
- });
+ await render(
+ hbs`<TabPageHeader @model={{this.model}} @breadcrumbs={{this.breadcrumbs}} @handleSearch={{this.handleSearch}} @handleInput={{this.handleInput}} @handleKeyDown={{this.handleKeyDown}}/>`,
+ {
+ owner: this.engine,
+ }
+ );
assert.dom('[data-test-tab="overview"]').hasText('Overview', 'Overview tab renders');
assert.dom('[data-test-tab="roles"]').hasText('Roles', 'Roles tab renders');
assert.dom('[data-test-tab="config"]').hasText('Configuration', 'Configuration tab renders');
@@ -62,16 +76,16 @@ module('Integration | Component | kubernetes | TabPageHeader', function (hooks)
test('it should render filter for roles', async function (assert) {
await render(
- hbs`<TabPageHeader @model={{this.model}} @filterRoles={{true}} @rolesFilterValue="test" @breadcrumbs={{this.breadcrumbs}} />`,
+ hbs`<TabPageHeader @model={{this.model}} @filterRoles={{true}} @query="test" @breadcrumbs={{this.breadcrumbs}} @handleSearch={{this.handleSearch}} @handleInput={{this.handleInput}} @handleKeyDown={{this.handleKeyDown}} />`,
{ owner: this.engine }
);
- assert.dom('[data-test-nav-input] input').hasValue('test', 'Filter renders with provided value');
+ assert.dom(GENERAL.filterInputExplicit).hasValue('test', 'Filter renders with provided value');
});
test('it should yield block for toolbar actions', async function (assert) {
await render(
hbs`
- <TabPageHeader @model={{this.model}} @breadcrumbs={{this.breadcrumbs}}>
+ <TabPageHeader @model={{this.model}} @breadcrumbs={{this.breadcrumbs}} @handleSearch={{this.handleSearch}} @handleInput={{this.handleInput}} @handleKeyDown={{this.handleKeyDown}}>
<span data-test-yield>It yields!</span>
</TabPageHeader>
`,
<82990506+hc-github-team-secure-vault-core@users.noreply.github.com>
158ad050a77f2c321a1c7a398a0bee89d5c24b85 (#27310)
Dockerfile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
ui/tests/integration/components/filter-input-explicit-test.js+61 0
@@ -0,0 +1,61 @@
+/**
+ * Copyright (c) HashiCorp, Inc.
+ * SPDX-License-Identifier: BUSL-1.1
+ */
+
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import { render, typeIn, click } from '@ember/test-helpers';
+import hbs from 'htmlbars-inline-precompile';
+import { GENERAL } from 'vault/tests/helpers/general-selectors';
+import sinon from 'sinon';
+
+const handler = (e) => {
+ // required because filter-input-explicit passes handleSearch on form submit
+ if (e && e.preventDefault) e.preventDefault();
+ return;
+};
+
+module('Integration | Component | filter-input-explicit', function (hooks) {
+ setupRenderingTest(hooks);
+
+ hooks.beforeEach(function () {
+ this.handleSearch = sinon.spy(handler);
+ this.handleInput = sinon.spy();
+ this.handleKeyDown = sinon.spy();
+ this.query = '';
+ this.placeholder = 'Filter roles';
+
+ this.renderComponent = () => {
+ return render(
+ hbs`<FilterInputExplicit aria-label="test-component" @placeholder={{this.placeholder}} @query={{this.query}} @handleSearch={{this.handleSearch}} @handleInput={{this.handleInput}} @handleKeyDown={{this.handleKeyDown}} />`
+ );
+ };
+ });
+
+ test('it renders', async function (assert) {
+ this.query = 'foo';
+ await this.renderComponent();
+
+ assert
+ .dom(GENERAL.filterInputExplicit)
+ .hasAttribute('placeholder', 'Filter roles', 'Placeholder passed to input element');
+ assert.dom(GENERAL.filterInputExplicit).hasValue('foo', 'Value passed to input element');
+ });
+
+ test('it should call handleSearch on submit', async function (assert) {
+ await this.renderComponent();
+ await typeIn(GENERAL.filterInputExplicit, 'bar');
+ await click(GENERAL.filterInputExplicitSearch);
+ assert.ok(this.handleSearch.calledOnce, 'handleSearch was called once');
+ });
+
+ test('it should send keydown event on keydown', async function (assert) {
+ await this.renderComponent();
+ await typeIn(GENERAL.filterInputExplicit, 'a');
+ await typeIn(GENERAL.filterInputExplicit, 'b');
+
+ assert.ok(this.handleKeyDown.calledTwice, 'handle keydown was called twice');
+ assert.ok(this.handleSearch.notCalled, 'handleSearch was not called on a keydown event');
+ });
+});
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.
+
+![Entity](/img/vault-entity-waf1.png)
+
+**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.
sdk/database/dbplugin/database_grpc.pb.go+23 13
@@ -3,7 +3,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
-// - protoc-gen-go-grpc v1.3.0
+// - protoc-gen-go-grpc v1.4.0
// - protoc (unknown)
// source: sdk/database/dbplugin/database.proto
@@ -18,8 +18,8 @@ import (
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
-// Requires gRPC-Go v1.32.0 or later.
-const _ = grpc.SupportPackageIsVersion7
+// Requires gRPC-Go v1.62.0 or later.
+const _ = grpc.SupportPackageIsVersion8
const (
Database_Type_FullMethodName = "/dbplugin.Database/Type"
@@ -60,8 +60,9 @@ func NewDatabaseClient(cc grpc.ClientConnInterface) DatabaseClient {
}
func (c *databaseClient) Type(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*TypeResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(TypeResponse)
- err := c.cc.Invoke(ctx, Database_Type_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Database_Type_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -69,8 +70,9 @@ func (c *databaseClient) Type(ctx context.Context, in *Empty, opts ...grpc.CallO
}
func (c *databaseClient) CreateUser(ctx context.Context, in *CreateUserRequest, opts ...grpc.CallOption) (*CreateUserResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(CreateUserResponse)
- err := c.cc.Invoke(ctx, Database_CreateUser_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Database_CreateUser_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -78,8 +80,9 @@ func (c *databaseClient) CreateUser(ctx context.Context, in *CreateUserRequest,
}
func (c *databaseClient) RenewUser(ctx context.Context, in *RenewUserRequest, opts ...grpc.CallOption) (*Empty, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(Empty)
- err := c.cc.Invoke(ctx, Database_RenewUser_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Database_RenewUser_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -87,8 +90,9 @@ func (c *databaseClient) RenewUser(ctx context.Context, in *RenewUserRequest, op
}
func (c *databaseClient) RevokeUser(ctx context.Context, in *RevokeUserRequest, opts ...grpc.CallOption) (*Empty, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(Empty)
- err := c.cc.Invoke(ctx, Database_RevokeUser_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Database_RevokeUser_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -96,8 +100,9 @@ func (c *databaseClient) RevokeUser(ctx context.Context, in *RevokeUserRequest,
}
func (c *databaseClient) RotateRootCredentials(ctx context.Context, in *RotateRootCredentialsRequest, opts ...grpc.CallOption) (*RotateRootCredentialsResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(RotateRootCredentialsResponse)
- err := c.cc.Invoke(ctx, Database_RotateRootCredentials_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Database_RotateRootCredentials_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -105,8 +110,9 @@ func (c *databaseClient) RotateRootCredentials(ctx context.Context, in *RotateRo
}
func (c *databaseClient) Init(ctx context.Context, in *InitRequest, opts ...grpc.CallOption) (*InitResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(InitResponse)
- err := c.cc.Invoke(ctx, Database_Init_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Database_Init_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -114,8 +120,9 @@ func (c *databaseClient) Init(ctx context.Context, in *InitRequest, opts ...grpc
}
func (c *databaseClient) Close(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(Empty)
- err := c.cc.Invoke(ctx, Database_Close_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Database_Close_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -123,8 +130,9 @@ func (c *databaseClient) Close(ctx context.Context, in *Empty, opts ...grpc.Call
}
func (c *databaseClient) SetCredentials(ctx context.Context, in *SetCredentialsRequest, opts ...grpc.CallOption) (*SetCredentialsResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(SetCredentialsResponse)
- err := c.cc.Invoke(ctx, Database_SetCredentials_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Database_SetCredentials_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -132,8 +140,9 @@ func (c *databaseClient) SetCredentials(ctx context.Context, in *SetCredentialsR
}
func (c *databaseClient) GenerateCredentials(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*GenerateCredentialsResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GenerateCredentialsResponse)
- err := c.cc.Invoke(ctx, Database_GenerateCredentials_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Database_GenerateCredentials_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -142,8 +151,9 @@ func (c *databaseClient) GenerateCredentials(ctx context.Context, in *Empty, opt
// Deprecated: Do not use.
func (c *databaseClient) Initialize(ctx context.Context, in *InitializeRequest, opts ...grpc.CallOption) (*Empty, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(Empty)
- err := c.cc.Invoke(ctx, Database_Initialize_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Database_Initialize_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
website/content/docs/configuration/listener/tcp/tcp-tls.mdx+208 0
@@ -0,0 +1,208 @@
+---
+layout: docs
+page_title: Configure TLS for your Vault TCP listener
+description: >-
+ Example TCP listener configuration with TLS encryption.
+---
+
+# Configure TLS for your Vault TCP listener
+
+You can configure your TCP listener to use specific versions of TLS and specific
+ciphersuites.
+
+## Assumptions
+
+- **Your Vault instance is not currently running**. If your Vault cluster is
+ running, you must
+ [restart the cluster gracefully](https://support.hashicorp.com/hc/en-us/articles/17169701076371-A-Step-by-Step-Guide-to-Restarting-a-Vault-Cluster)
+ to apply changes to your TCP listener. SIGHIP will not reload your TLS
+ configuration.
+- **You have a valid TLS certificate file**.
+- **You have a valid TLS key file**.
+- **You have a valid CA file (if required)**.
+
+## Example TLS 1.3 configuration
+
+If a reasonably modern set of clients are connecting to a Vault instance, you
+can configure the `tcp` listener stanza to only accept TLS 1.3 with the
+`tls_min_version` parameter:
+
+<CodeBlockConfig hideClipboard highlight="5">
+
+```plaintext
+listener "tcp" {
+ address = "127.0.0.1:8200"
+ tls_cert_file = "cert.pem"
+ tls_key_file = "key.pem"
+ tls_min_version = "tls13"
+}
+```
+
+</CodeBlockConfig>
+
+Vault does not accept explicit ciphersuite configuration for TLS 1.3 because the
+Go team has already designated a select set of ciphers that align with the
+broadly-accepted Mozilla Security/Server Side TLS guidance for [modern TLS
+configuration](https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility).
+
+## Example TLS 1.2 configuration
+
+To use TLS 1.2 with a non-default set of ciphersuites, you can set 1.2 as the
+minimum and maximum allowed TLS version and explicitly define your preferred
+ciphersuites with `tls_ciper_suites` and one or more of the ciphersuite
+constants from the ciphersuite configuration parser. For example:
+
+<CodeBlockConfig hideClipboard highlight="5-7">
+
+```plaintext
+listener "tcp" {
+ address = "127.0.0.1:8200"
+ tls_cert_file = "cert.pem"
+ tls_key_file = "key.pem"
+ tls_min_version = "tls12"
+ tls_max_version = "tls12"
+ tls_cipher_suites = "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"
+}
+```
+
+</CodeBlockConfig>
+
+You must set the minimum and maximum TLS version to disable TLS 1.3, which does
+not support explicit cipher selection. The priority order of the ciphersuites
+in `tls_cipher_suites` is determined by the `tls` Go package.
+
+<Note>
+
+ The TLS 1.2 configuration example excludes any 3DES ciphers to avoid potential
+ exposure to the Sweet32 attack (CVE-2016-2183). You should customize the
+ ciphersuite list as needed to meet your environment-specific security
+ requirements.
+
+</Note>
+
+## Verify your TLS configuration
+
+You can verify your TLS configuration using an SSL scanner such as
+[`sslscan`](https://github.com/rbsec/sslscan).
+
+<Tabs>
+<Tab heading="Example scan with ECDSA certificate">
+
+<CodeBlockConfig hideClipboard>
+
+```shell-session
+$ sslscan 127.0.0.1:8200
+Version: 2.1.3
+OpenSSL 3.2.1 30 Jan 2024
+
+Connected to 127.0.0.1
+
+Testing SSL server 127.0.0.1 on port 8200 using SNI name 127.0.0.1
+
+ SSL/TLS Protocols:
+SSLv2 disabled
+SSLv3 disabled
+TLSv1.0 disabled
+TLSv1.1 disabled
+TLSv1.2 enabled
+TLSv1.3 enabled
+
+ TLS Fallback SCSV:
+Server supports TLS Fallback SCSV
+
+ TLS renegotiation:
+Session renegotiation not supported
+
+ TLS Compression:
+Compression disabled
+
+ Heartbleed:
+TLSv1.3 not vulnerable to heartbleed
+TLSv1.2 not vulnerable to heartbleed
+
+ Supported Server Cipher(s):
+Preferred TLSv1.3 128 bits TLS_AES_128_GCM_SHA256 Curve 25519 DHE 253
+Accepted TLSv1.3 256 bits TLS_AES_256_GCM_SHA384 Curve 25519 DHE 253
+Accepted TLSv1.3 256 bits TLS_CHACHA20_POLY1305_SHA256 Curve 25519 DHE 253
+Preferred TLSv1.2 128 bits ECDHE-ECDSA-AES128-GCM-SHA256 Curve 25519 DHE 253
+Accepted TLSv1.2 256 bits ECDHE-ECDSA-AES256-GCM-SHA384 Curve 25519 DHE 253
+Accepted TLSv1.2 256 bits ECDHE-ECDSA-CHACHA20-POLY1305 Curve 25519 DHE 253
+Accepted TLSv1.2 128 bits ECDHE-ECDSA-AES128-SHA Curve 25519 DHE 253
+Accepted TLSv1.2 256 bits ECDHE-ECDSA-AES256-SHA Curve 25519 DHE 253
+
+ Server Key Exchange Group(s):
+TLSv1.3 128 bits secp256r1 (NIST P-256)
+TLSv1.3 192 bits secp384r1 (NIST P-384)
+TLSv1.3 260 bits secp521r1 (NIST P-521)
+TLSv1.3 128 bits x25519
+TLSv1.2 128 bits secp256r1 (NIST P-256)
+TLSv1.2 192 bits secp384r1 (NIST P-384)
+TLSv1.2 260 bits secp521r1 (NIST P-521)
+TLSv1.2 128 bits x25519
+
+ SSL Certificate:
+Signature Algorithm: ecdsa-with-SHA256
+ECC Curve Name: prime256v1
+ECC Key Strength: 128
+
+Subject: localhost
+Issuer: localhost
+
+Not valid before: May 17 17:27:29 2024 GMT
+Not valid after: Jun 16 17:27:29 2024 GMT
+```
+
+</CodeBlockConfig>
+
+</Tab>
+<Tab heading="Example scan with RSA certificate">
+
+<CodeBlockConfig hideClipboard>
+
+```shell-session
+sslscan 127.0.0.1:8200
+Testing SSL server 127.0.0.1 on port 8200 using SNI name 127.0.0.1
+
+ SSL/TLS Protocols:
+SSLv2 disabled
+SSLv3 disabled
+TLSv1.0 disabled
+TLSv1.1 disabled
+TLSv1.2 enabled
+TLSv1.3 enabled
+
+ Supported Server Cipher(s):
+Preferred TLSv1.3 128 bits TLS_AES_128_GCM_SHA256 Curve 25519 DHE 253
+Accepted TLSv1.3 256 bits TLS_AES_256_GCM_SHA384 Curve 25519 DHE 253
+Accepted TLSv1.3 256 bits TLS_CHACHA20_POLY1305_SHA256 Curve 25519 DHE 253
+Preferred TLSv1.2 128 bits ECDHE-RSA-AES128-GCM-SHA256 Curve 25519 DHE 253
+Accepted TLSv1.2 256 bits ECDHE-RSA-AES256-GCM-SHA384 Curve 25519 DHE 253
+Accepted TLSv1.2 256 bits ECDHE-RSA-CHACHA20-POLY1305 Curve 25519 DHE 253
+Accepted TLSv1.2 128 bits ECDHE-RSA-AES128-SHA Curve 25519 DHE 253
+Accepted TLSv1.2 256 bits ECDHE-RSA-AES256-SHA Curve 25519 DHE 253
+Accepted TLSv1.2 128 bits AES128-GCM-SHA256
+Accepted TLSv1.2 256 bits AES256-GCM-SHA384
+Accepted TLSv1.2 128 bits AES128-SHA
+Accepted TLSv1.2 256 bits AES256-SHA
+Accepted TLSv1.2 112 bits TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
+Accepted TLSv1.2 112 bits TLS_RSA_WITH_3DES_EDE_CBC_SHA
+
+ Server Key Exchange Group(s):
+TLSv1.3 128 bits secp256r1 (NIST P-256)
+TLSv1.3 192 bits secp384r1 (NIST P-384)
+TLSv1.3 260 bits secp521r1 (NIST P-521)
+TLSv1.3 128 bits x25519
+TLSv1.2 128 bits secp256r1 (NIST P-256)
+TLSv1.2 192 bits secp384r1 (NIST P-384)
+TLSv1.2 260 bits secp521r1 (NIST P-521)
+TLSv1.2 128 bits x25519
+
+ SSL Certificate:
+Signature Algorithm: sha256WithRSAEncryption
+RSA Key Strength: 4096
+```
+
+</CodeBlockConfig>
+
+</Tab>
+</Tabs>
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 (on
signed certificate. This field is validated against `allowed_user_ids` on
the 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 (on
signed certificate. This field is validated against `allowed_user_ids` on
the 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 the
signed 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
ui/app/services/permissions.js+32 19
@@ -99,7 +99,7 @@ export default class PermissionsService extends Service {
@service store;
@service namespace;
- get baseNs() {
+ get fullCurrentNamespace() {
const currentNs = this.namespace.path;
return this.chrootNamespace
? `${sanitizePath(this.chrootNamespace)}/${sanitizePath(currentNs)}`
@@ -122,24 +122,37 @@ export default class PermissionsService extends Service {
}
}
- get wildcardPath() {
- const ns = [sanitizePath(this.chrootNamespace), sanitizePath(this.namespace.userRootNamespace)].join('/');
- // wildcard path comes back from root namespace as empty string,
- // but within a namespace it's the namespace itself ending with a slash
- return ns === '/' ? '' : `${sanitizePath(ns)}/`;
- }
-
/**
- * hasWildcardAccess checks if the user has a wildcard policy
+ * hasWildcardNsAccess checks if the user has a wildcard access to target namespace
+ * via full glob path or any ancestors of the target namespace
+ * @param {string} targetNs is the current/target namespace that we are checking access for
* @param {object} globPaths key is path, value is object with capabilities
* @returns {boolean} whether the user's policy includes wildcard access to NS
*/
- hasWildcardAccess(globPaths = {}) {
- // First check if the wildcard path is in the globPaths object
- if (!Object.keys(globPaths).includes(this.wildcardPath)) return false;
+ hasWildcardNsAccess(targetNs, globPaths = {}) {
+ const nsParts = sanitizePath(targetNs).split('/');
+ let matchKey = null;
+ // For each section of the namespace, check if there is a matching wildcard path
+ while (nsParts.length > 0) {
+ // glob paths always end in a slash
+ const test = `${nsParts.join('/')}/`;
+ if (Object.keys(globPaths).includes(test)) {
+ matchKey = test;
+ break;
+ }
+ nsParts.pop();
+ }
+ // Finally, check if user has wildcard access to the root namespace
+ // which is represented by an empty string
+ if (!matchKey && Object.keys(globPaths).includes('')) {
+ matchKey = '';
+ }
+ if (null === matchKey) {
+ return false;
+ }
- // if so, make sure the current namespace is a child of the wildcard path
- return this.namespace.path.startsWith(this.wildcardPath);
+ // if there is a match make sure the capabilities do not include deny
+ return !this.isDenied(globPaths[matchKey]);
}
// This method is called to recalculate whether to show the permissionsBanner when the namespace changes
@@ -148,14 +161,14 @@ export default class PermissionsService extends Service {
this.permissionsBanner = null;
return;
}
- const namespace = this.baseNs;
+ const namespace = this.fullCurrentNamespace;
const allowed =
// check if the user has wildcard access to the relative root namespace
- this.hasWildcardAccess(this.globPaths) ||
+ this.hasWildcardNsAccess(namespace, this.globPaths) ||
// or if any of their glob paths start with the namespace
- Object.keys(this.globPaths).any((k) => k.startsWith(namespace)) ||
+ Object.keys(this.globPaths).any((k) => k.startsWith(namespace) && !this.isDenied(this.globPaths[k])) ||
// or if any of their exact paths start with the namespace
- Object.keys(this.exactPaths).any((k) => k.startsWith(namespace));
+ Object.keys(this.exactPaths).any((k) => k.startsWith(namespace) && !this.isDenied(this.exactPaths[k]));
this.permissionsBanner = allowed ? null : PERMISSIONS_BANNER_STATES.noAccess;
}
@@ -200,7 +213,7 @@ export default class PermissionsService extends Service {
}
pathNameWithNamespace(pathName) {
- const namespace = this.baseNs;
+ const namespace = this.fullCurrentNamespace;
if (namespace) {
return `${sanitizePath(namespace)}/${sanitizeStart(pathName)}`;
} else {
ui/lib/kubernetes/addon/components/page/roles.hbs+4 1
@@ -6,8 +6,11 @@
<TabPageHeader
@model={{@backend}}
@filterRoles={{not @promptConfig}}
- @rolesFilterValue={{@filterValue}}
+ @query={{this.query}}
@breadcrumbs={{@breadcrumbs}}
+ @handleSearch={{this.handleSearch}}
+ @handleInput={{this.handleInput}}
+ @handleKeyDown={{this.handleKeyDown}}
>
{{#unless @promptConfig}}
<ToolbarLink @route="roles.create" @type="add" data-test-toolbar-roles-action>
ui/lib/kubernetes/addon/components/tab-page-header.hbs+5 3
@@ -28,10 +28,12 @@
<Toolbar aria-label="items for managing kubernetes items">
{{#if @filterRoles}}
<ToolbarFilters>
- <NavigateInput
- @filter={{@rolesFilterValue}}
+ <FilterInputExplicit
+ @query={{@query}}
@placeholder="Filter roles"
- @urls={{hash list="vault.cluster.secrets.backend.kubernetes.roles"}}
+ @handleSearch={{@handleSearch}}
+ @handleInput={{@handleInput}}
+ @handleKeyDown={{@handleKeyDown}}
/>
</ToolbarFilters>
{{/if}}
sdk/database/dbplugin/v5/proto/database_grpc.pb.go+15 9
@@ -3,7 +3,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
-// - protoc-gen-go-grpc v1.3.0
+// - protoc-gen-go-grpc v1.4.0
// - protoc (unknown)
// source: sdk/database/dbplugin/v5/proto/database.proto
@@ -18,8 +18,8 @@ import (
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
-// Requires gRPC-Go v1.32.0 or later.
-const _ = grpc.SupportPackageIsVersion7
+// Requires gRPC-Go v1.62.0 or later.
+const _ = grpc.SupportPackageIsVersion8
const (
Database_Initialize_FullMethodName = "/dbplugin.v5.Database/Initialize"
@@ -51,8 +51,9 @@ func NewDatabaseClient(cc grpc.ClientConnInterface) DatabaseClient {
}
func (c *databaseClient) Initialize(ctx context.Context, in *InitializeRequest, opts ...grpc.CallOption) (*InitializeResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(InitializeResponse)
- err := c.cc.Invoke(ctx, Database_Initialize_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Database_Initialize_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -60,8 +61,9 @@ func (c *databaseClient) Initialize(ctx context.Context, in *InitializeRequest,
}
func (c *databaseClient) NewUser(ctx context.Context, in *NewUserRequest, opts ...grpc.CallOption) (*NewUserResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(NewUserResponse)
- err := c.cc.Invoke(ctx, Database_NewUser_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Database_NewUser_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -69,8 +71,9 @@ func (c *databaseClient) NewUser(ctx context.Context, in *NewUserRequest, opts .
}
func (c *databaseClient) UpdateUser(ctx context.Context, in *UpdateUserRequest, opts ...grpc.CallOption) (*UpdateUserResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(UpdateUserResponse)
- err := c.cc.Invoke(ctx, Database_UpdateUser_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Database_UpdateUser_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -78,8 +81,9 @@ func (c *databaseClient) UpdateUser(ctx context.Context, in *UpdateUserRequest,
}
func (c *databaseClient) DeleteUser(ctx context.Context, in *DeleteUserRequest, opts ...grpc.CallOption) (*DeleteUserResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(DeleteUserResponse)
- err := c.cc.Invoke(ctx, Database_DeleteUser_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Database_DeleteUser_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -87,8 +91,9 @@ func (c *databaseClient) DeleteUser(ctx context.Context, in *DeleteUserRequest,
}
func (c *databaseClient) Type(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*TypeResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(TypeResponse)
- err := c.cc.Invoke(ctx, Database_Type_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Database_Type_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -96,8 +101,9 @@ func (c *databaseClient) Type(ctx context.Context, in *Empty, opts ...grpc.CallO
}
func (c *databaseClient) Close(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(Empty)
- err := c.cc.Invoke(ctx, Database_Close_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, Database_Close_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
ui/app/models/pki/role.js+80 61
@@ -4,76 +4,20 @@
*/
import Model, { attr } from '@ember-data/model';
+import { service } from '@ember/service';
import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities';
import { withModelValidations } from 'vault/decorators/model-validations';
-import { withFormFields } from 'vault/decorators/model-form-fields';
+import { withExpandedAttributes } from 'vault/decorators/model-expanded-attributes';
const validations = {
name: [{ type: 'presence', message: 'Name is required.' }],
};
-const fieldGroups = [
- {
- default: [
- 'name',
- 'issuerRef',
- 'customTtl',
- 'notBeforeDuration',
- 'maxTtl',
- 'generateLease',
- 'noStore',
- 'addBasicConstraints',
- ],
- },
- {
- 'Domain handling': [
- 'allowedDomains',
- 'allowedDomainsTemplate',
- 'allowBareDomains',
- 'allowSubdomains',
- 'allowGlobDomains',
- 'allowWildcardCertificates',
- 'allowLocalhost', // default: true (returned true by OpenApi)
- 'allowAnyName',
- 'enforceHostnames', // default: true (returned true by OpenApi)
- ],
- },
- {
- 'Key parameters': ['keyType', 'keyBits', 'signatureBits'],
- },
- {
- 'Key usage': ['keyUsage', 'extKeyUsage', 'extKeyUsageOids'],
- },
- { 'Policy identifiers': ['policyIdentifiers'] },
- {
- 'Subject Alternative Name (SAN) Options': [
- 'allowIpSans',
- 'allowedUriSans',
- 'allowUriSansTemplate',
- 'allowedOtherSans',
- ],
- },
- {
- 'Additional subject fields': [
- 'allowedUserIds',
- 'allowedSerialNumbers',
- 'requireCn',
- 'useCsrCommonName',
- 'useCsrSans',
- 'ou',
- 'organization',
- 'country',
- 'locality',
- 'province',
- 'streetAddress',
- 'postalCode',
- ],
- },
-];
-
-@withFormFields(null, fieldGroups)
+@withExpandedAttributes()
@withModelValidations(validations)
export default class PkiRoleModel extends Model {
+ @service version; // noStoreMetadata is enterprise-only, so we need this available
+
get useOpenAPI() {
// must be a getter so it can be accessed in path-help.js
return true;
@@ -84,6 +28,73 @@ export default class PkiRoleModel extends Model {
@attr('string', { readOnly: true }) backend;
+ get formFieldGroups() {
+ let defaultArray = [
+ 'name',
+ 'issuerRef',
+ 'customTtl',
+ 'notBeforeDuration',
+ 'maxTtl',
+ 'generateLease',
+ 'noStore',
+ 'noStoreMetadata',
+ 'addBasicConstraints',
+ ];
+ if (this.version.isCommunity) {
+ const entFields = ['noStoreMetadata'];
+ defaultArray = defaultArray.filter((field) => !entFields.includes(field));
+ }
+ return this._expandGroups([
+ {
+ default: defaultArray,
+ },
+ {
+ 'Domain handling': [
+ 'allowedDomains',
+ 'allowedDomainsTemplate',
+ 'allowBareDomains',
+ 'allowSubdomains',
+ 'allowGlobDomains',
+ 'allowWildcardCertificates',
+ 'allowLocalhost', // default: true (returned true by OpenApi)
+ 'allowAnyName',
+ 'enforceHostnames', // default: true (returned true by OpenApi)
+ ],
+ },
+ {
+ 'Key parameters': ['keyType', 'keyBits', 'signatureBits'],
+ },
+ {
+ 'Key usage': ['keyUsage', 'extKeyUsage', 'extKeyUsageOids'],
+ },
+ { 'Policy identifiers': ['policyIdentifiers'] },
+ {
+ 'Subject Alternative Name (SAN) Options': [
+ 'allowIpSans',
+ 'allowedUriSans',
+ 'allowUriSansTemplate',
+ 'allowedOtherSans',
+ ],
+ },
+ {
+ 'Additional subject fields': [
+ 'allowedUserIds',
+ 'allowedSerialNumbers',
+ 'requireCn',
+ 'useCsrCommonName',
+ 'useCsrSans',
+ 'ou',
+ 'organization',
+ 'country',
+ 'locality',
+ 'province',
+ 'streetAddress',
+ 'postalCode',
+ ],
+ },
+ ]);
+ }
+
/* Overriding OpenApi default options */
@attr('string', {
label: 'Role name',
@@ -146,6 +157,14 @@ export default class PkiRoleModel extends Model {
})
noStore;
+ @attr('boolean', {
+ label: 'Do not store certificate metadata in storage backend',
+ detailsLabel: 'Store metadata in storage backend', // template reverses value
+ subText:
+ 'We don’t recommend storing metadata, since this information creates overhead in storage, and requires clean up.',
+ })
+ noStoreMetadata;
+
@attr('boolean', {
label: 'Basic constraints valid for non-CA',
detailsLabel: 'Add basic constraints',
vault/request_forwarding_service_grpc.pb.go+11 8
@@ -3,7 +3,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
-// - protoc-gen-go-grpc v1.3.0
+// - protoc-gen-go-grpc v1.4.0
// - protoc (unknown)
// source: vault/request_forwarding_service.proto
@@ -19,8 +19,8 @@ import (
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
-// Requires gRPC-Go v1.32.0 or later.
-const _ = grpc.SupportPackageIsVersion7
+// Requires gRPC-Go v1.62.0 or later.
+const _ = grpc.SupportPackageIsVersion8
const (
RequestForwarding_ForwardRequest_FullMethodName = "/vault.RequestForwarding/ForwardRequest"
@@ -46,8 +46,9 @@ func NewRequestForwardingClient(cc grpc.ClientConnInterface) RequestForwardingCl
}
func (c *requestForwardingClient) ForwardRequest(ctx context.Context, in *forwarding.Request, opts ...grpc.CallOption) (*forwarding.Response, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(forwarding.Response)
- err := c.cc.Invoke(ctx, RequestForwarding_ForwardRequest_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, RequestForwarding_ForwardRequest_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -55,8 +56,9 @@ func (c *requestForwardingClient) ForwardRequest(ctx context.Context, in *forwar
}
func (c *requestForwardingClient) Echo(ctx context.Context, in *EchoRequest, opts ...grpc.CallOption) (*EchoReply, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(EchoReply)
- err := c.cc.Invoke(ctx, RequestForwarding_Echo_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, RequestForwarding_Echo_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -64,11 +66,12 @@ func (c *requestForwardingClient) Echo(ctx context.Context, in *EchoRequest, opt
}
func (c *requestForwardingClient) PerformanceStandbyElectionRequest(ctx context.Context, in *PerfStandbyElectionInput, opts ...grpc.CallOption) (RequestForwarding_PerformanceStandbyElectionRequestClient, error) {
- stream, err := c.cc.NewStream(ctx, &RequestForwarding_ServiceDesc.Streams[0], RequestForwarding_PerformanceStandbyElectionRequest_FullMethodName, opts...)
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ stream, err := c.cc.NewStream(ctx, &RequestForwarding_ServiceDesc.Streams[0], RequestForwarding_PerformanceStandbyElectionRequest_FullMethodName, cOpts...)
if err != nil {
return nil, err
}
- x := &requestForwardingPerformanceStandbyElectionRequestClient{stream}
+ x := &requestForwardingPerformanceStandbyElectionRequestClient{ClientStream: stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
@@ -172,7 +175,7 @@ func _RequestForwarding_PerformanceStandbyElectionRequest_Handler(srv interface{
if err := stream.RecvMsg(m); err != nil {
return err
}
- return srv.(RequestForwardingServer).PerformanceStandbyElectionRequest(m, &requestForwardingPerformanceStandbyElectionRequestServer{stream})
+ return srv.(RequestForwardingServer).PerformanceStandbyElectionRequest(m, &requestForwardingPerformanceStandbyElectionRequestServer{ServerStream: stream})
}
type RequestForwarding_PerformanceStandbyElectionRequestServer interface {
<82990506+hc-github-team-secure-vault-core@users.noreply.github.com>
5acc4331ea74efb37d90d53b1e104eecb71e8458 (#27337)
website/content/api-docs/auth/azure.mdx | 12 +++-
website/content/api-docs/secret/azure.mdx | 10 +++
website/content/docs/auth/azure.mdx | 84 ++++++++++++++++++++++
website/content/docs/secrets/azure.mdx | 86 +++++++++++++++++++++++
4 files changed, 191 insertions(+), 1 deletion(-)
website/content/docs/configuration/storage/raft.mdx+2 2
@@ -143,8 +143,8 @@ delay) mode. The maximum allowed value is 10.
can use this to increase the number of mounts and namespaces that can be
stored without the risk of other storage entries becoming too large. All other
notes on [`max_entry_size`](#max-entry-size) apply. Before changing this, read
- the [/vault/docs/enterprise/namespaces/namespace-limits](Run Vault Enterprise
- with many namespaces) guide regarding important performance considerations.
+ the [Run Vault Enterprise
+ with many namespaces](/vault/docs/enterprise/namespaces/namespace-limits) guide regarding important performance considerations.
- `autopilot_reconcile_interval` `(string: "10s")` - This is the interval after
which autopilot will pick up any state changes. State change could mean multiple
<82990506+hc-github-team-secure-vault-core@users.noreply.github.com>
b8d482c2fa698c9660db1fbea539c39ffc3270c4 (#27284)
website/content/docs/enterprise/sentinel/properties.mdx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
ui/tests/integration/components/pki/page/pki-role-details-test.js+18 1
@@ -20,6 +20,7 @@ module('Integration | Component | pki role details page', function (hooks) {
name: 'Foobar',
backend: 'pki',
noStore: false,
+ noStoreMetadata: true,
keyUsage: [],
extKeyUsage: ['bar', 'baz'],
ttl: 600,
@@ -27,7 +28,6 @@ module('Integration | Component | pki role details page', function (hooks) {
});
test('it should render the page component', async function (assert) {
- assert.expect(5);
await render(
hbs`
<Page::PkiRoleDetails @role={{this.model}} />
@@ -44,9 +44,26 @@ module('Integration | Component | pki role details page', function (hooks) {
assert
.dom(PKI_ROLE_DETAILS.noStoreValue)
.containsText('Yes', 'noStore shows opposite of what the value is');
+ assert
+ .dom(PKI_ROLE_DETAILS.noStoreMetadataValue)
+ .doesNotExist('does not render value for enterprise-only field');
assert.dom(PKI_ROLE_DETAILS.customTtlValue).containsText('10 minutes', 'TTL shown as duration');
});
+ test('it should render the enterprise-only values in enterprise edition', async function (assert) {
+ const version = this.owner.lookup('service:version');
+ version.type = 'enterprise';
+ await render(
+ hbs`
+ <Page::PkiRoleDetails @role={{this.model}} />
+ `,
+ { owner: this.engine }
+ );
+ assert
+ .dom(PKI_ROLE_DETAILS.noStoreMetadataValue)
+ .containsText('No', 'noStoreMetadata shows opposite of what the value is');
+ });
+
test('it should render the notAfter date if present', async function (assert) {
assert.expect(1);
this.model = this.store.createRecord('pki/role', {
More files changed — see the full commit.

References