From a3041982aa784f8d7e429e19bbcc3b567cbb98d5 Mon Sep 17 00:00:00 2001 From: Angel Garbarino Date: Tue, 14 Dec 2021 17:17:00 -0700 Subject: [PATCH] UI/merge main (#13436) * Rename master key to root key (#13324) * See what it looks like to replace "master key" with "root key". There are two places that would require more challenging code changes: the storage path `core/master`, and its contents (the JSON-serialized EncodedKeyringtructure.) * Restore accidentally deleted line * Add changelog * Update root->recovery * Fix test Co-authored-by: Nick Cabatoff * Fix typo (#13355) * Add kms_library configuration stanza (#13352) - Add the kms_library configuration stanza to Vault's command/server - Provide validation of keys and general configuration. - Add initial kms_library configuration documentation - Attempt at startup to verify we can read the configured HSM Library - Hook in KmsLibrary config into the Validate to detect typo/unused keys * modifed note (#13351) * Incorporate Ember Flight Icons (#12976) * adds ember-flight-icons dependecy * adds inline-json-import babel plugin * adds flight icon styling * updates Icon component to support flight icons * updates Icon component usages to new api and updates name values to flight icon set when available * fixes tests * updates icon story with flight mappings and fixes issue with flight icons not rendering in storybook * adds changelog * fixes typo in sign action glyph name in transit-key model * adds comments to icon-map * updates Icon component to use only supported flight icon sizes * adds icon transform codemod * updates icon transform formatting to handle edge case * runs icon transform on templates * updates Icon usage in toolbar-filter md and story * updates tests * docs: winsvc update recommendations (#13280) * docs: update custom database sample code (#13211) * clarify more sink options (#12586) * Update @hashicorp/react-hashi-stack-menu (#13354) * Docs to clarify k8s auth options with short-lived tokens (#13275) * Rework 1.21 content into one heading and add note at top * Add notes about extended k8s token duration * Add example of ClusterRoleBinding for using client JWTs * Adds support for SHA-3 to transit (#13367) * Adding support for SHA3 in the transit backend. * Adds SHA-3 tests for transit sign/verify path. Adds SHA-3 tests for logical system tools path hash functionality. Updates documentation to include SHA-3 algorithms in system tools path hashing. * Adds changelog entry. Co-authored-by: robison jacka * agent/cache: differentiate open log messages (#13362) Changes the error output for the second open of the persistent cache file, to differentiate it from the c.UI.Error message for the initial open of the cache file, just to make it easier to tell where a problem occurred. * Warn user supplying nonce values in FIPS mode for transit encryption requests (#13366) * Warn user supplying nonce values in FIPS mode for transit encryption requests - Send back a warning within the response if an end-user supplies nonce values that we use within the various transit encrypt apis. - We do not send a warning if an end-user supplies a nonce value but we don't use it. - Affected api methods are encrypt, rewrap and datakey - The warning is only sent when we are operating in FIPS mode. * [VAULT-3252] Add entity-alias behavior change to docs (#13370) * Add entity-alias behavior change to docs * Add upgrade note about entity-alias mapping change * Rename 1.7-9 upgrade pages, shuffle upgrade note position * Update website/content/partials/entity-alias-mapping.mdx Co-authored-by: Meggie * Add incorrect policy issue to the docs * Add example about entity-alias restriction Co-authored-by: Meggie * VAULT-1564 report in-flight requests (#13024) * VAULT-1564 report in-flight requests * adding a changelog * Changing some variable names and fixing comments * minor style change * adding unauthenticated support for in-flight-req * adding documentation for the listener.profiling stanza * adding an atomic counter for the inflight requests addressing comments * addressing comments * logging completed requests * fixing a test * providing log_requests_info as a config option to determine at which level requests should be logged * removing a member and a method from the StatusHeaderResponseWriter struct * adding api docks * revert changes in NewHTTPResponseWriter * Fix logging invalid log_requests_info value * Addressing comments * Fixing a test * use an tomic value for logRequestsInfo, and moving the CreateClientID function to Core * fixing go.sum * minor refactoring * protecting InFlightRequests from data race * another try on fixing a data race * another try to fix a data race * addressing comments * fixing couple of tests * changing log_requests_info to log_requests_level * minor style change * fixing a test * removing the lock in InFlightRequests * use single-argument form for interface assertion * adding doc for the new configuration paramter * adding the new doc to the nav data file * minor fix * auth/jwt: Update plugin to v0.11.3 (#13365) * auth/jwt: Update plugin to v0.11.3 * add changelog * changelog++ * Update alert banner (#13375) * Updating website for 1.9.1 (#13378) * Use os.Hostname instead of a dependency that doesn't work on OpenBSD. (#13389) * Remove another use gopsutil/host. (#13390) * CLI changes for new mount tune config parameter allowed_managed_keys (#13255) * CLI changes for new mount tune config parameter allowed_managed_keys * Correct allowed_managed_keys description in auth and secrets * Documentation update for secrets and removed changes for auth * Add changelog and remove documentation changes for auth * removed changelog * Correct the field description * auth/jwt: update changelog for pkce improvement (#13392) * Fix test validating convergent encryption behaviour across key types (#13371) - The test was attempting to test the convergent encryption behaviour with several key types but the common function never used the passed in key type. So we ran the test with the default aes256-gcm96 only. * Fix managed namespace test (#13394) * Fix managed namespace test * Remove log * Some changelog tidying for 1.10 preview (#13385) * Some changelog tidying for 1.10 preview * PR accounted for by different CL entry * changelog++ Working on a new workflow for generating the preview so I thought I'd leave a note that it's still coming. * UI/fix client count partial (#13396) * Initial fix * Add fallback zero values * Add changelog * Fix client count current test * Support clearing an identity alias' custom_metadata (#13395) * Support clearing an identity alias' custom_metadata Previously, an update to an entity alias supported updating the custom_metadata as long as the update was not empty, which makes it impossible to clear the metadata values completely. Fixes: - empty custom_metadata parameters are honoured on entity alias update - update related tests - drop dependency on mapstructure - reformat with gofumpt * Docs: fix invalid link in the kubernetes auth api doc. (#13399) * Clean up whitespace * auth/azure: add note about debug env (#13405) * auth/azure: add note about debug env * Update azure.mdx * Update azure.mdx * Add universal default key_bits value for PKI endpoints (#13080) * Allow universal default for key_bits This allows the key_bits field to take a universal default value, 0, which, depending on key_type, gets adjusted appropriately into a specific default value (rsa->2048, ec->256, ignored under ed25519). Signed-off-by: Alexander Scheel * Handle universal default key size in certutil Also move RSA < 2048 error message into certutil directly, instead of in ca_util/path_roles. Signed-off-by: Alexander Scheel * Add missing RSA key sizes to pki/backend_test.go Signed-off-by: Alexander Scheel * Switch to returning updated values When determining the default, don't pass in pointer types, but instead return the newly updated value. Signed-off-by: Alexander Scheel * Add changelog entry Signed-off-by: Alexander Scheel * Re-add fix for ed25519 from #13254 Ed25519 internally specifies a hash length; by changing the default from 256 to 0, we fail validation in ValidateSignatureLength(...) unless we specify the key algorithm. Signed-off-by: Alexander Scheel * Fix logging statement using formatting args (#13407) * Add docs about path param restrictions (#13413) * Add docs about path param restrictions * Update website/content/api-docs/auth/userpass.mdx Co-authored-by: Loann Le <84412881+taoism4504@users.noreply.github.com> * Update with review suggestion Co-authored-by: Loann Le <84412881+taoism4504@users.noreply.github.com> * Update raftautosnapshots.mdx (#13412) * Main go version bump (#13408) * Go 1.17.2 -> 1.17.5 * Switching to cimg * Bump yarn cache key version so that it uses the new disk layout we've adopted for using cimg/go. (#13420) * Add vault-api module (#13048) * crt main fix for ecr tag (#13425) * Add no-op method setupManagedKeyRegistry(). (#13433) * github auth: use org id to verify creds (#13332) * github auth: use org id to verify creds * add check for required org param; add test case * update UTs * add nil check for org * add changelog * fix typo in ut * set org ID if it is unset; add more ut coverage * add optional organization_id * move client instantiation * refactor parse URL; add UT for setting org ID * fix comment in UT * add nil check * don't update org name on change; return warning * refactor verifyCredentials * error when unable to fetch org ID on config write; add warnings * fix bug in log message * update UT and small refactor * update comments and log msg * use getter for org ID Co-authored-by: Jim Kalafut Co-authored-by: Nick Cabatoff Co-authored-by: Harsimran Singh Maan Co-authored-by: Steven Clark Co-authored-by: Loann Le <84412881+taoism4504@users.noreply.github.com> Co-authored-by: Jordan Reimer Co-authored-by: mickael-hc <86245626+mickael-hc@users.noreply.github.com> Co-authored-by: Calvin Leung Huang <1883212+calvn@users.noreply.github.com> Co-authored-by: Mike Green <772413+mikegreen@users.noreply.github.com> Co-authored-by: Noel Quiles <3746694+EnMod@users.noreply.github.com> Co-authored-by: Tom Proctor Co-authored-by: Matt Schultz <975680+schultz-is@users.noreply.github.com> Co-authored-by: robison jacka Co-authored-by: Theron Voran Co-authored-by: Pratyoy Mukhopadhyay <35388175+pmmukh@users.noreply.github.com> Co-authored-by: Meggie Co-authored-by: hghaf099 <83242695+hghaf099@users.noreply.github.com> Co-authored-by: John-Michael Faircloth Co-authored-by: Brandon Romano Co-authored-by: divyapola5 <87338962+divyapola5@users.noreply.github.com> Co-authored-by: Chelsea Shaw <82459713+hashishaw@users.noreply.github.com> Co-authored-by: Ben Ash <32777270+benashz@users.noreply.github.com> Co-authored-by: Jason O'Donnell <2160810+jasonodonnell@users.noreply.github.com> Co-authored-by: Alexander Scheel Co-authored-by: Mark Lewis <56076038+ml4@users.noreply.github.com> Co-authored-by: Sai Hemanth Bheemreddy <35338241+SaiHemanthBR@users.noreply.github.com> Co-authored-by: Kyle Penfound Co-authored-by: Victor Rodriguez --- .circleci/config.yml | 114 ++++----- .circleci/config/commands/@caches.yml | 11 +- .circleci/config/commands/go_test.yml | 14 +- .circleci/config/commands/setup-go.yml | 2 +- .circleci/config/executors/@executors.yml | 22 +- .github/workflows/build.yml | 8 +- CHANGELOG.md | 61 +++++ Makefile | 2 +- README.md | 2 +- builtin/credential/github/backend_test.go | 4 + builtin/credential/github/path_config.go | 80 ++++++- builtin/credential/github/path_config_test.go | 214 +++++++++++++++++ builtin/credential/github/path_login.go | 111 +++++---- builtin/credential/github/path_login_test.go | 186 +++++++++++++++ builtin/logical/pki/backend_test.go | 10 +- builtin/logical/pki/ca_util.go | 8 +- builtin/logical/pki/fields.go | 11 +- builtin/logical/pki/path_roles.go | 15 +- builtin/logical/transit/backend_test.go | 2 + builtin/logical/transit/path_datakey.go | 6 +- builtin/logical/transit/path_encrypt.go | 38 +++ builtin/logical/transit/path_encrypt_test.go | 80 +++++++ builtin/logical/transit/path_hash.go | 14 ++ builtin/logical/transit/path_hash_test.go | 18 ++ builtin/logical/transit/path_hmac.go | 4 + builtin/logical/transit/path_hmac_test.go | 18 ++ builtin/logical/transit/path_rewrap.go | 11 +- builtin/logical/transit/path_sign_verify.go | 8 + .../logical/transit/path_sign_verify_test.go | 20 ++ changelog/12792.txt | 4 +- changelog/12795.txt | 4 +- changelog/12976.txt | 3 + changelog/13022.txt | 4 +- changelog/13024.txt | 3 + changelog/13080.txt | 3 + changelog/13178.txt | 2 +- changelog/13202.txt | 3 - changelog/13324.txt | 3 + changelog/13332.txt | 3 + changelog/13365.txt | 3 + changelog/13367.txt | 3 + changelog/13395.txt | 3 + changelog/13396.txt | 3 + changelog/13408.txt | 3 + command/agent.go | 2 +- command/commands.go | 2 + command/debug.go | 65 ++++- command/debug_test.go | 5 + command/operator_init.go | 14 +- command/operator_rekey.go | 6 +- command/operator_seal.go | 4 +- command/operator_unseal.go | 4 +- command/secrets_enable.go | 14 ++ command/secrets_enable_test.go | 4 + command/secrets_tune.go | 14 ++ command/secrets_tune_test.go | 4 + command/server.go | 3 + command/server/config.go | 120 ++++++++++ command/server/config_oss_test.go | 4 + command/server/config_test_helpers.go | 24 ++ command/server/listener_test.go | 13 + command/server/test-fixtures/config3.hcl | 1 + .../test-fixtures/unauth_in_flight_access.hcl | 9 + go.mod | 2 +- go.sum | 4 +- helper/constants/fips.go | 8 + http/handler.go | 46 ++++ http/handler_test.go | 39 +++ http/logical.go | 2 +- http/sys_config_state_test.go | 1 + http/sys_in_flight_requests.go | 23 ++ http/sys_in_flight_requests_test.go | 46 ++++ internalshared/configutil/listener.go | 28 ++- physical/raft/raft.go | 4 +- scripts/docker/Dockerfile | 2 +- scripts/docker/Dockerfile.ui | 2 +- sdk/helper/certutil/helpers.go | 107 +++++++-- sdk/helper/keysutil/consts.go | 14 ++ sdk/helper/keysutil/policy.go | 16 ++ sdk/logical/request.go | 6 + sdk/logical/response.go | 8 +- sdk/logical/token.go | 59 ++++- sdk/logical/token_test.go | 60 +++++ ui/.storybook/config.js | 4 + ui/app/components/clients/dashboard.js | 6 +- ui/app/components/regex-validator.hbs | 2 +- ui/app/components/status-menu.js | 9 +- ui/app/components/toolbar-secret-link.js | 2 +- ui/app/models/transit-key.js | 22 +- ui/app/routes/vault/cluster.js | 4 +- .../styles/components/console-ui-panel.scss | 2 +- .../components/{hs-icon.scss => icon.scss} | 7 + ui/app/styles/core.scss | 2 +- ui/app/templates/components/alert-popup.hbs | 8 +- .../components/clients/dashboard.hbs | 8 +- ui/app/templates/components/cluster-info.hbs | 8 +- .../components/console/command-input.hbs | 5 +- .../components/console/log-command.hbs | 2 +- .../console/log-error-with-html.hbs | 2 +- .../components/console/log-error.hbs | 2 +- .../templates/components/console/log-help.hbs | 2 +- .../components/console/log-success.hbs | 2 +- .../templates/components/console/ui-panel.hbs | 2 +- ui/app/templates/components/control-group.hbs | 6 +- .../components/database-connection.hbs | 2 +- .../components/file-to-array-buffer.hbs | 4 +- .../generate-credentials-database.hbs | 2 +- .../components/generated-item-list.hbs | 2 +- .../components/hover-copy-button.hbs | 2 +- .../components/identity/item-aliases.hbs | 8 +- .../components/identity/item-groups.hbs | 4 +- .../components/identity/item-members.hbs | 12 +- .../identity/item-parent-groups.hbs | 5 +- ui/app/templates/components/json-editor.hbs | 4 +- .../templates/components/kv-object-editor.hbs | 3 +- ui/app/templates/components/license-info.hbs | 24 +- ui/app/templates/components/menu-sidebar.hbs | 4 +- .../components/mount-backend-form.hbs | 3 +- .../templates/components/namespace-picker.hbs | 4 +- ui/app/templates/components/nav-header.hbs | 7 +- ui/app/templates/components/pgp-file.hbs | 7 +- .../components/raft-storage-overview.hbs | 6 +- .../components/secret-create-or-update.hbs | 6 +- .../templates/components/secret-form-show.hbs | 4 +- .../components/secret-list-header.hbs | 2 +- .../components/secret-list/aws-role-item.hbs | 2 +- .../secret-list/database-list-item.hbs | 2 +- .../templates/components/secret-list/item.hbs | 4 +- .../components/secret-list/pki-cert-item.hbs | 2 +- .../components/secret-list/pki-role-item.hbs | 2 +- .../components/secret-list/ssh-role-item.hbs | 2 +- .../secret-list/transform-list-item.hbs | 11 +- .../transform-transformation-item.hbs | 11 +- .../components/secret-version-menu.hbs | 6 +- .../templates/components/selectable-card.hbs | 4 +- ui/app/templates/components/status-menu.hbs | 2 +- ui/app/templates/components/text-file.hbs | 6 +- ui/app/templates/components/tool-unwrap.hbs | 5 +- .../components/toolbar-secret-link.hbs | 2 +- .../transform-show-transformation.hbs | 4 +- .../components/transit-form-show.hbs | 20 +- .../components/transit-key-action/datakey.hbs | 6 +- .../components/transit-key-action/decrypt.hbs | 2 +- .../components/transit-key-action/encrypt.hbs | 2 +- .../components/transit-key-action/export.hbs | 2 +- .../components/transit-key-action/hmac.hbs | 2 +- .../components/transit-key-action/rewrap.hbs | 2 +- .../components/transit-key-action/sign.hbs | 2 +- .../templates/components/wizard-content.hbs | 2 +- .../templates/components/wizard-progress.hbs | 5 +- .../templates/components/wizard-section.hbs | 4 +- .../templates/components/wizard/ad-engine.hbs | 2 +- .../components/wizard/alicloud-engine.hbs | 2 +- .../components/wizard/alicloud-method.hbs | 2 +- .../components/wizard/approle-method.hbs | 2 +- .../components/wizard/auth-details.hbs | 2 +- .../components/wizard/aws-engine.hbs | 2 +- .../components/wizard/aws-method.hbs | 2 +- .../components/wizard/azure-engine.hbs | 2 +- .../components/wizard/azure-method.hbs | 2 +- .../components/wizard/cert-method.hbs | 2 +- .../components/wizard/features-selection.hbs | 6 +- .../components/wizard/gcp-engine.hbs | 2 +- .../components/wizard/gcp-method.hbs | 2 +- .../components/wizard/gcpkms-engine.hbs | 2 +- .../components/wizard/kmip-engine.hbs | 2 +- .../components/wizard/kubernetes-method.hbs | 2 +- .../templates/components/wizard/kv-engine.hbs | 2 +- .../components/wizard/ldap-method.hbs | 2 +- .../components/wizard/okta-method.hbs | 2 +- .../components/wizard/pki-engine.hbs | 2 +- .../components/wizard/radius-method.hbs | 2 +- .../components/wizard/replication-setup.hbs | 5 +- .../components/wizard/secrets-display.hbs | 4 +- .../components/wizard/ssh-engine.hbs | 2 +- .../components/wizard/totp-engine.hbs | 2 +- .../components/wizard/transit-engine.hbs | 2 +- .../components/wizard/tutorial-complete.hbs | 2 +- .../components/wizard/tutorial-error.hbs | 2 +- .../components/wizard/tutorial-idle.hbs | 2 +- .../components/wizard/tutorial-paused.hbs | 2 +- .../components/wizard/userpass-method.hbs | 2 +- ui/app/templates/vault.hbs | 8 +- ui/app/templates/vault/cluster.hbs | 4 +- .../cluster/access/identity/aliases/index.hbs | 8 +- .../vault/cluster/access/identity/index.hbs | 8 +- .../vault/cluster/access/leases/list.hbs | 8 +- .../vault/cluster/access/methods.hbs | 18 +- ui/app/templates/vault/cluster/init.hbs | 2 +- .../vault/cluster/policies/index.hbs | 14 +- .../replication-dr-promote/details.hbs | 2 +- .../cluster/replication-dr-promote/index.hbs | 2 +- .../backend/transit-actions-layout.hbs | 2 +- .../cluster/secrets/backend/versions.hbs | 14 +- ui/app/templates/vault/cluster/unseal.hbs | 2 +- ui/app/templates/vault/not-found.hbs | 2 +- ui/ember-cli-build.js | 2 +- ui/lib/core/addon/components/icon.js | 47 ++-- .../core/addon/components/toolbar-filters.js | 2 +- ui/lib/core/addon/components/toolbar-link.js | 6 +- ui/lib/core/addon/helpers/cluster-states.js | 18 +- ui/lib/core/addon/helpers/message-types.js | 6 +- .../templates/components/alert-banner.hbs | 10 +- .../templates/components/alert-inline.hbs | 2 +- .../addon/templates/components/box-radio.hbs | 8 +- .../addon/templates/components/chevron.hbs | 3 +- .../templates/components/confirm-action.hbs | 2 +- .../templates/components/confirm/message.hbs | 2 +- .../templates/components/empty-state.hbs | 2 +- .../addon/templates/components/form-error.hbs | 4 +- .../addon/templates/components/form-field.hbs | 2 +- .../core/addon/templates/components/icon.hbs | 14 +- .../templates/components/info-table-row.hbs | 12 +- .../templates/components/info-tooltip.hbs | 3 +- .../components/linkable-item/content.hbs | 3 +- .../templates/components/masked-input.hbs | 4 +- .../core/addon/templates/components/modal.hbs | 3 +- .../templates/components/navigate-input.hbs | 2 +- .../addon/templates/components/popup-menu.hbs | 2 +- .../components/replication-dashboard.hbs | 6 +- .../components/replication-mode-summary.hbs | 7 +- .../components/replication-secondary-card.hbs | 4 +- .../components/search-select-placeholder.hbs | 2 +- .../templates/components/search-select.hbs | 2 +- .../templates/components/string-list.hbs | 2 +- .../templates/components/toggle-button.hbs | 4 +- .../templates/components/toolbar-link.hbs | 4 +- .../addon/templates/components/ttl-form.hbs | 7 +- .../templates/components/ttl-picker2.hbs | 6 +- ui/lib/core/icon-mappings.js | 222 +++++++++++++++++ ui/lib/core/package.json | 4 +- ui/lib/core/stories/icon.md | 8 +- ui/lib/core/stories/icon.stories.js | 78 +++++- .../core/stories/toolbar/toolbar-filters.md | 7 +- .../toolbar/toolbar-filters.stories.js | 2 +- .../core/stories/toolbar/toolbar.stories.js | 2 +- .../templates/components/header-scope.hbs | 4 +- .../components/operation-field-display.hbs | 2 +- ui/lib/kmip/addon/templates/configure.hbs | 4 +- .../addon/templates/credentials/index.hbs | 2 +- ui/lib/kmip/addon/templates/scope/roles.hbs | 2 +- ui/lib/kmip/addon/templates/scopes/index.hbs | 2 +- .../addon/templates/components/swagger-ui.hbs | 10 +- .../components/path-filter-config-list.hbs | 16 +- .../components/replication-primary-card.hbs | 2 +- .../components/replication-summary.hbs | 12 +- ui/package.json | 5 +- ui/scripts/codemods/icon-transform.js | 70 ++++++ ui/tests/acceptance/managed-namespace-test.js | 6 +- .../integration/components/chevron-test.js | 6 +- .../components/clients-current-test.js | 21 +- ui/tests/integration/components/icon-test.js | 35 ++- .../components/info-table-row-test.js | 14 +- .../replication-primary-card-test.js | 4 +- .../components/toolbar-link-test.js | 2 +- ui/yarn.lock | 35 ++- vault/activity_log.go | 66 +----- vault/activity_log_test.go | 91 ++----- vault/barrier.go | 42 ++-- vault/barrier_aes_gcm.go | 86 +++---- vault/barrier_test.go | 12 +- vault/core.go | 118 ++++++++- vault/core_metrics.go | 9 + vault/generate_root.go | 8 +- vault/generate_root_recovery.go | 4 +- vault/ha.go | 14 +- vault/identity_store_aliases.go | 32 ++- vault/identity_store_aliases_test.go | 208 ++++++++++++---- vault/init.go | 18 +- vault/keyring.go | 28 +-- vault/keyring_test.go | 14 +- vault/logical_system.go | 51 +++- vault/logical_system_paths.go | 35 ++- vault/logical_system_test.go | 16 +- vault/managed_key_registry.go | 7 + vault/raft.go | 8 +- vault/rekey.go | 54 ++--- vault/request_forwarding_rpc.go | 6 +- vault/request_handling.go | 26 +- vault/seal.go | 4 +- vault/seal/seal.go | 4 +- vault/seal_testing.go | 2 +- vault/seal_testing_util.go | 2 +- website/content/api-docs/auth/kubernetes.mdx | 5 +- website/content/api-docs/auth/userpass.mdx | 2 +- website/content/api-docs/index.mdx | 5 + website/content/api-docs/libraries.mdx | 6 + website/content/api-docs/secret/transit.mdx | 16 ++ .../content/api-docs/system/in-flight-req.mdx | 41 ++++ .../system/storage/raftautosnapshots.mdx | 2 +- website/content/api-docs/system/tools.mdx | 4 + .../docs/agent/autoauth/sinks/file.mdx | 3 + website/content/docs/agent/index.mdx | 2 +- website/content/docs/agent/winsvc.mdx | 3 + website/content/docs/auth/azure.mdx | 12 + .../content/docs/auth/jwt/oidc_providers.mdx | 214 +++++++++++++++++ website/content/docs/auth/kubernetes.mdx | 196 ++++++++++++--- .../content/docs/commands/secrets/enable.mdx | 5 + .../content/docs/commands/secrets/tune.mdx | 7 +- website/content/docs/concepts/identity.mdx | 6 +- website/content/docs/concepts/tokens.mdx | 5 +- .../docs/configuration/kms-library.mdx | 59 +++++ .../docs/configuration/listener/tcp.mdx | 3 + .../docs/configuration/log-requests-level.mdx | 31 +++ website/content/docs/internals/telemetry.mdx | 1 + .../content/docs/secrets/databases/custom.mdx | 18 +- ...rade-to-1.7.0.mdx => upgrade-to-1.7.x.mdx} | 9 +- ...rade-to-1.8.0.mdx => upgrade-to-1.8.x.mdx} | 9 +- ...rade-to-1.9.0.mdx => upgrade-to-1.9.x.mdx} | 9 +- .../content/partials/entity-alias-mapping.mdx | 7 + website/data/alert-banner.js | 11 +- website/data/api-docs-nav-data.json | 4 + website/data/docs-nav-data.json | 20 +- website/data/version.js | 4 +- website/package-lock.json | 224 +++++------------- website/package.json | 2 +- 316 files changed, 3848 insertions(+), 1273 deletions(-) create mode 100644 builtin/credential/github/path_config_test.go create mode 100644 builtin/credential/github/path_login_test.go create mode 100644 changelog/12976.txt create mode 100644 changelog/13024.txt create mode 100644 changelog/13080.txt delete mode 100644 changelog/13202.txt create mode 100644 changelog/13324.txt create mode 100644 changelog/13332.txt create mode 100644 changelog/13365.txt create mode 100644 changelog/13367.txt create mode 100644 changelog/13395.txt create mode 100644 changelog/13396.txt create mode 100644 changelog/13408.txt create mode 100644 command/server/test-fixtures/unauth_in_flight_access.hcl create mode 100644 helper/constants/fips.go create mode 100644 http/sys_in_flight_requests.go create mode 100644 http/sys_in_flight_requests_test.go rename ui/app/styles/components/{hs-icon.scss => icon.scss} (84%) create mode 100644 ui/lib/core/icon-mappings.js create mode 100644 ui/scripts/codemods/icon-transform.js create mode 100644 vault/managed_key_registry.go create mode 100644 website/content/api-docs/system/in-flight-req.mdx create mode 100644 website/content/docs/configuration/kms-library.mdx create mode 100644 website/content/docs/configuration/log-requests-level.mdx rename website/content/docs/upgrading/{upgrade-to-1.7.0.mdx => upgrade-to-1.7.x.mdx} (91%) rename website/content/docs/upgrading/{upgrade-to-1.8.0.mdx => upgrade-to-1.8.x.mdx} (93%) rename website/content/docs/upgrading/{upgrade-to-1.9.0.mdx => upgrade-to-1.9.x.mdx} (95%) create mode 100644 website/content/partials/entity-alias-mapping.mdx diff --git a/.circleci/config.yml b/.circleci/config.yml index f9e59a9acfff..d1bd416b053e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,11 +9,11 @@ jobs: docker: - image: docker.mirror.hashicorp.services/node:14-buster shell: /usr/bin/env bash -euo pipefail -c - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault steps: - checkout - restore_cache: - key: yarn-lock-v6-{{ checksum "ui/yarn.lock" }} + key: yarn-lock-v7-{{ checksum "ui/yarn.lock" }} name: Restore yarn cache - run: command: | @@ -22,7 +22,7 @@ jobs: npm rebuild node-sass name: Install UI dependencies - save_cache: - key: yarn-lock-v6-{{ checksum "ui/yarn.lock" }} + key: yarn-lock-v7-{{ checksum "ui/yarn.lock" }} name: Save yarn cache paths: - ui/node_modules @@ -30,7 +30,7 @@ jobs: docker: - image: docker.mirror.hashicorp.services/node:14-buster shell: /usr/bin/env bash -euo pipefail -c - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault resource_class: xlarge steps: - run: @@ -49,7 +49,7 @@ jobs: working_directory: ~/ - checkout - restore_cache: - key: yarn-lock-v6-{{ checksum "ui/yarn.lock" }} + key: yarn-lock-v7-{{ checksum "ui/yarn.lock" }} name: Restore yarn cache - attach_workspace: at: . @@ -83,12 +83,12 @@ jobs: docker: - image: docker.mirror.hashicorp.services/node:14-buster shell: /usr/bin/env bash -euo pipefail -c - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault resource_class: xlarge steps: - checkout - restore_cache: - key: yarn-lock-v6-{{ checksum "ui/yarn.lock" }} + key: yarn-lock-v7-{{ checksum "ui/yarn.lock" }} name: Restore yarn cache - attach_workspace: at: . @@ -101,7 +101,7 @@ jobs: build-go-dev: machine: true shell: /usr/bin/env bash -euo pipefail -c - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault steps: - run: command: | @@ -111,7 +111,7 @@ jobs: sudo rm -rf /usr/local/go sudo tar -C /usr/local -xzf "go${GO_VERSION}.linux-amd64.tar.gz" rm -f "go${GO_VERSION}.linux-amd64.tar.gz" - GOPATH="/go" + GOPATH="/home/circleci/go" mkdir $GOPATH 2>/dev/null || { sudo mkdir $GOPATH && sudo chmod 777 $GOPATH; } echo "export GOPATH='$GOPATH'" >> "$BASH_ENV" echo "export PATH='$PATH:$GOPATH/bin:/usr/local/go/bin'" >> "$BASH_ENV" @@ -125,7 +125,7 @@ jobs: - checkout - restore_cache: keys: - - v1.3-{{checksum "go.sum"}}-{{checksum "sdk/go.sum"}}-{{checksum "api/go.sum"}} + - v1.4-{{checksum "go.sum"}}-{{checksum "sdk/go.sum"}}-{{checksum "api/go.sum"}} name: Restore exact go modules cache - attach_workspace: at: . @@ -145,7 +145,7 @@ jobs: environment: - CIRCLECI_CLI_VERSION: 0.1.5546 - GO_TAGS: '' - - GO_VERSION: 1.17.2 + - GO_VERSION: 1.17.5 - GOTESTSUM_VERSION: 0.5.2 algolia-index: docker: @@ -164,9 +164,9 @@ jobs: name: Push content to Algolia Index test-go-remote-docker: docker: - - image: docker.mirror.hashicorp.services/circleci/golang:1.17.2-buster + - image: docker.mirror.hashicorp.services/cimg/go:1.17.5 resource_class: medium - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault parallelism: 8 steps: - run: @@ -199,7 +199,7 @@ jobs: - go-test-cache-date-v1-{{ checksum "/tmp/go-cache-key" }} - restore_cache: keys: - - v1.3-{{checksum "go.sum"}}-{{checksum "sdk/go.sum"}}-{{checksum "api/go.sum"}} + - v1.4-{{checksum "go.sum"}}-{{checksum "sdk/go.sum"}}-{{checksum "api/go.sum"}} name: Restore exact go modules cache - run: command: | @@ -297,20 +297,20 @@ jobs: -e NO_PROXY \ -e VAULT_TEST_LOG_DIR=/tmp/testlogs \ --network vaulttest --name \ - testcontainer docker.mirror.hashicorp.services/circleci/golang:1.17.2-buster \ + testcontainer docker.mirror.hashicorp.services/cimg/go:1.17.5 \ tail -f /dev/null # Run tests test -d /tmp/go-cache && docker cp /tmp/go-cache testcontainer:/tmp/gocache - docker exec testcontainer sh -c 'mkdir -p /go/src/github.com/hashicorp/vault' - docker cp . testcontainer:/go/src/github.com/hashicorp/vault/ + docker exec testcontainer sh -c 'mkdir -p /home/circleci/go/src/github.com/hashicorp/vault' + docker cp . testcontainer:/home/circleci/go/src/github.com/hashicorp/vault/ docker cp $DOCKER_CERT_PATH/ testcontainer:$DOCKER_CERT_PATH # Copy the downloaded modules inside the container. - docker exec testcontainer sh -c 'mkdir -p /go/pkg' - docker cp "$(go env GOPATH)/pkg/mod" testcontainer:/go/pkg/mod + docker exec testcontainer sh -c 'mkdir -p /home/circleci/go/pkg' + docker cp "$(go env GOPATH)/pkg/mod" testcontainer:/home/circleci/go/pkg/mod - docker exec -w /go/src/github.com/hashicorp/vault/ \ + docker exec -w /home/circleci/go/src/github.com/hashicorp/vault/ \ -e CIRCLECI -e VAULT_CI_GO_TEST_RACE \ -e GOCACHE=/tmp/gocache \ -e GO_TAGS \ @@ -346,7 +346,7 @@ jobs: no_output_timeout: 60m - run: command: | - docker cp testcontainer:/go/src/github.com/hashicorp/vault/test-results . + docker cp testcontainer:/home/circleci/go/src/github.com/hashicorp/vault/test-results . docker cp testcontainer:/tmp/gocache /tmp/go-cache name: Copy test results when: always @@ -361,9 +361,9 @@ jobs: - GO_TAGS: '' test-go-race: docker: - - image: docker.mirror.hashicorp.services/circleci/golang:1.17.2-buster + - image: docker.mirror.hashicorp.services/cimg/go:1.17.5 resource_class: xlarge - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault parallelism: 8 steps: - run: @@ -393,7 +393,7 @@ jobs: - go-test-cache-date-v1-{{ checksum "/tmp/go-cache-key" }} - restore_cache: keys: - - v1.3-{{checksum "go.sum"}}-{{checksum "sdk/go.sum"}}-{{checksum "api/go.sum"}} + - v1.4-{{checksum "go.sum"}}-{{checksum "sdk/go.sum"}}-{{checksum "api/go.sum"}} name: Restore exact go modules cache - run: command: | @@ -490,20 +490,20 @@ jobs: -e NO_PROXY \ -e VAULT_TEST_LOG_DIR=/tmp/testlogs \ --network vaulttest --name \ - testcontainer docker.mirror.hashicorp.services/circleci/golang:1.17.2-buster \ + testcontainer docker.mirror.hashicorp.services/cimg/go:1.17.5 \ tail -f /dev/null # Run tests test -d /tmp/go-cache && docker cp /tmp/go-cache testcontainer:/tmp/gocache - docker exec testcontainer sh -c 'mkdir -p /go/src/github.com/hashicorp/vault' - docker cp . testcontainer:/go/src/github.com/hashicorp/vault/ + docker exec testcontainer sh -c 'mkdir -p /home/circleci/go/src/github.com/hashicorp/vault' + docker cp . testcontainer:/home/circleci/go/src/github.com/hashicorp/vault/ docker cp $DOCKER_CERT_PATH/ testcontainer:$DOCKER_CERT_PATH # Copy the downloaded modules inside the container. - docker exec testcontainer sh -c 'mkdir -p /go/pkg' - docker cp "$(go env GOPATH)/pkg/mod" testcontainer:/go/pkg/mod + docker exec testcontainer sh -c 'mkdir -p /home/circleci/go/pkg' + docker cp "$(go env GOPATH)/pkg/mod" testcontainer:/home/circleci/go/pkg/mod - docker exec -w /go/src/github.com/hashicorp/vault/ \ + docker exec -w /home/circleci/go/src/github.com/hashicorp/vault/ \ -e CIRCLECI -e VAULT_CI_GO_TEST_RACE \ -e GOCACHE=/tmp/gocache \ -e GO_TAGS \ @@ -571,9 +571,9 @@ jobs: name: Build Docker Image if Necessary test-go: docker: - - image: docker.mirror.hashicorp.services/circleci/golang:1.17.2-buster + - image: docker.mirror.hashicorp.services/cimg/go:1.17.5 resource_class: large - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault parallelism: 8 steps: - run: @@ -603,7 +603,7 @@ jobs: - go-test-cache-date-v1-{{ checksum "/tmp/go-cache-key" }} - restore_cache: keys: - - v1.3-{{checksum "go.sum"}}-{{checksum "sdk/go.sum"}}-{{checksum "api/go.sum"}} + - v1.4-{{checksum "go.sum"}}-{{checksum "sdk/go.sum"}}-{{checksum "api/go.sum"}} name: Restore exact go modules cache - run: command: | @@ -700,20 +700,20 @@ jobs: -e NO_PROXY \ -e VAULT_TEST_LOG_DIR=/tmp/testlogs \ --network vaulttest --name \ - testcontainer docker.mirror.hashicorp.services/circleci/golang:1.17.2-buster \ + testcontainer docker.mirror.hashicorp.services/cimg/go:1.17.5 \ tail -f /dev/null # Run tests test -d /tmp/go-cache && docker cp /tmp/go-cache testcontainer:/tmp/gocache - docker exec testcontainer sh -c 'mkdir -p /go/src/github.com/hashicorp/vault' - docker cp . testcontainer:/go/src/github.com/hashicorp/vault/ + docker exec testcontainer sh -c 'mkdir -p /home/circleci/go/src/github.com/hashicorp/vault' + docker cp . testcontainer:/home/circleci/go/src/github.com/hashicorp/vault/ docker cp $DOCKER_CERT_PATH/ testcontainer:$DOCKER_CERT_PATH # Copy the downloaded modules inside the container. - docker exec testcontainer sh -c 'mkdir -p /go/pkg' - docker cp "$(go env GOPATH)/pkg/mod" testcontainer:/go/pkg/mod + docker exec testcontainer sh -c 'mkdir -p /home/circleci/go/pkg' + docker cp "$(go env GOPATH)/pkg/mod" testcontainer:/home/circleci/go/pkg/mod - docker exec -w /go/src/github.com/hashicorp/vault/ \ + docker exec -w /home/circleci/go/src/github.com/hashicorp/vault/ \ -e CIRCLECI -e VAULT_CI_GO_TEST_RACE \ -e GOCACHE=/tmp/gocache \ -e GO_TAGS \ @@ -759,7 +759,7 @@ jobs: pre-flight-checks: machine: true shell: /usr/bin/env bash -euo pipefail -c - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault steps: - run: command: | @@ -769,7 +769,7 @@ jobs: sudo rm -rf /usr/local/go sudo tar -C /usr/local -xzf "go${GO_VERSION}.linux-amd64.tar.gz" rm -f "go${GO_VERSION}.linux-amd64.tar.gz" - GOPATH="/go" + GOPATH="/home/circleci/go" mkdir $GOPATH 2>/dev/null || { sudo mkdir $GOPATH && sudo chmod 777 $GOPATH; } echo "export GOPATH='$GOPATH'" >> "$BASH_ENV" echo "export PATH='$PATH:$GOPATH/bin:/usr/local/go/bin'" >> "$BASH_ENV" @@ -810,9 +810,9 @@ jobs: git config --global url."git@github.com:".insteadOf https://github.com/ - restore_cache: keys: - - v1.3-{{checksum "go.sum"}}-{{checksum "sdk/go.sum"}}-{{checksum "api/go.sum"}} - - v1.3-{{checksum "go.sum"}}-{{checksum "sdk/go.sum"}} - - v1.3-{{checksum "go.sum"}} + - v1.4-{{checksum "go.sum"}}-{{checksum "sdk/go.sum"}}-{{checksum "api/go.sum"}} + - v1.4-{{checksum "go.sum"}}-{{checksum "sdk/go.sum"}} + - v1.4-{{checksum "go.sum"}} name: Restore closest matching go modules cache - run: command: | @@ -832,20 +832,20 @@ jobs: } name: Verify downloading modules did not modify any files - save_cache: - key: v1.3-{{checksum "go.sum"}}-{{checksum "sdk/go.sum"}}-{{checksum "api/go.sum"}} + key: v1.4-{{checksum "go.sum"}}-{{checksum "sdk/go.sum"}}-{{checksum "api/go.sum"}} name: Save go modules cache paths: - - /go/pkg/mod + - /home/circleci/go/pkg/mod environment: - CIRCLECI_CLI_VERSION: 0.1.5546 - GO_TAGS: '' - - GO_VERSION: 1.17.2 + - GO_VERSION: 1.17.5 - GOTESTSUM_VERSION: 0.5.2 test-go-race-remote-docker: docker: - - image: docker.mirror.hashicorp.services/circleci/golang:1.17.2-buster + - image: docker.mirror.hashicorp.services/cimg/go:1.17.5 resource_class: medium - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault parallelism: 8 steps: - run: @@ -878,7 +878,7 @@ jobs: - go-test-cache-date-v1-{{ checksum "/tmp/go-cache-key" }} - restore_cache: keys: - - v1.3-{{checksum "go.sum"}}-{{checksum "sdk/go.sum"}}-{{checksum "api/go.sum"}} + - v1.4-{{checksum "go.sum"}}-{{checksum "sdk/go.sum"}}-{{checksum "api/go.sum"}} name: Restore exact go modules cache - run: command: | @@ -976,20 +976,20 @@ jobs: -e NO_PROXY \ -e VAULT_TEST_LOG_DIR=/tmp/testlogs \ --network vaulttest --name \ - testcontainer docker.mirror.hashicorp.services/circleci/golang:1.17.2-buster \ + testcontainer docker.mirror.hashicorp.services/cimg/go:1.17.5 \ tail -f /dev/null # Run tests test -d /tmp/go-cache && docker cp /tmp/go-cache testcontainer:/tmp/gocache - docker exec testcontainer sh -c 'mkdir -p /go/src/github.com/hashicorp/vault' - docker cp . testcontainer:/go/src/github.com/hashicorp/vault/ + docker exec testcontainer sh -c 'mkdir -p /home/circleci/go/src/github.com/hashicorp/vault' + docker cp . testcontainer:/home/circleci/go/src/github.com/hashicorp/vault/ docker cp $DOCKER_CERT_PATH/ testcontainer:$DOCKER_CERT_PATH # Copy the downloaded modules inside the container. - docker exec testcontainer sh -c 'mkdir -p /go/pkg' - docker cp "$(go env GOPATH)/pkg/mod" testcontainer:/go/pkg/mod + docker exec testcontainer sh -c 'mkdir -p /home/circleci/go/pkg' + docker cp "$(go env GOPATH)/pkg/mod" testcontainer:/home/circleci/go/pkg/mod - docker exec -w /go/src/github.com/hashicorp/vault/ \ + docker exec -w /home/circleci/go/src/github.com/hashicorp/vault/ \ -e CIRCLECI -e VAULT_CI_GO_TEST_RACE \ -e GOCACHE=/tmp/gocache \ -e GO_TAGS \ @@ -1025,7 +1025,7 @@ jobs: no_output_timeout: 60m - run: command: | - docker cp testcontainer:/go/src/github.com/hashicorp/vault/test-results . + docker cp testcontainer:/home/circleci/go/src/github.com/hashicorp/vault/test-results . docker cp testcontainer:/tmp/gocache /tmp/go-cache name: Copy test results when: always diff --git a/.circleci/config/commands/@caches.yml b/.circleci/config/commands/@caches.yml index 03e04cbc2bd4..97f5d3b44072 100644 --- a/.circleci/config/commands/@caches.yml +++ b/.circleci/config/commands/@caches.yml @@ -2,7 +2,7 @@ restore_yarn_cache: steps: - restore_cache: name: Restore yarn cache - key: &YARN_LOCK_CACHE_KEY yarn-lock-v6-{{ checksum "ui/yarn.lock" }} + key: &YARN_LOCK_CACHE_KEY yarn-lock-v7-{{ checksum "ui/yarn.lock" }} save_yarn_cache: steps: - save_cache: @@ -12,14 +12,15 @@ save_yarn_cache: - ui/node_modules # allows restoring go mod caches by incomplete prefix. This is useful when re-generating # cache, but not when running builds and tests that require an exact match. +# TODO should we be including arch in cache key? restore_go_mod_cache_permissive: steps: - restore_cache: name: Restore closest matching go modules cache keys: - - &gocachekey v1.3-{{checksum "go.sum"}}-{{checksum "sdk/go.sum"}}-{{checksum "api/go.sum"}} - - v1.3-{{checksum "go.sum"}}-{{checksum "sdk/go.sum"}} - - v1.3-{{checksum "go.sum"}} + - &gocachekey v1.4-{{checksum "go.sum"}}-{{checksum "sdk/go.sum"}}-{{checksum "api/go.sum"}} + - v1.4-{{checksum "go.sum"}}-{{checksum "sdk/go.sum"}} + - v1.4-{{checksum "go.sum"}} restore_go_mod_cache: steps: - restore_cache: @@ -32,7 +33,7 @@ save_go_mod_cache: name: Save go modules cache key: *gocachekey paths: - - /go/pkg/mod + - /home/circleci/go/pkg/mod refresh_go_mod_cache: steps: - restore_go_mod_cache_permissive diff --git a/.circleci/config/commands/go_test.yml b/.circleci/config/commands/go_test.yml index 6ea817b12218..ca3f71f0d2a4 100644 --- a/.circleci/config/commands/go_test.yml +++ b/.circleci/config/commands/go_test.yml @@ -14,7 +14,7 @@ parameters: default: false go_image: type: string - default: "docker.mirror.hashicorp.services/circleci/golang:1.17.2-buster" + default: "docker.mirror.hashicorp.services/cimg/go:1.17.5" use_docker: type: boolean default: false @@ -139,15 +139,15 @@ steps: # Run tests test -d << parameters.cache_dir >> && docker cp << parameters.cache_dir >> testcontainer:/tmp/gocache - docker exec testcontainer sh -c 'mkdir -p /go/src/github.com/hashicorp/vault' - docker cp . testcontainer:/go/src/github.com/hashicorp/vault/ + docker exec testcontainer sh -c 'mkdir -p /home/circleci/go/src/github.com/hashicorp/vault' + docker cp . testcontainer:/home/circleci/go/src/github.com/hashicorp/vault/ docker cp $DOCKER_CERT_PATH/ testcontainer:$DOCKER_CERT_PATH # Copy the downloaded modules inside the container. - docker exec testcontainer sh -c 'mkdir -p /go/pkg' - docker cp "$(go env GOPATH)/pkg/mod" testcontainer:/go/pkg/mod + docker exec testcontainer sh -c 'mkdir -p /home/circleci/go/pkg' + docker cp "$(go env GOPATH)/pkg/mod" testcontainer:/home/circleci/go/pkg/mod - docker exec -w /go/src/github.com/hashicorp/vault/ \ + docker exec -w /home/circleci/go/src/github.com/hashicorp/vault/ \ -e CIRCLECI -e VAULT_CI_GO_TEST_RACE \ -e GOCACHE=/tmp/gocache \ -e GO_TAGS \ @@ -185,7 +185,7 @@ steps: name: Copy test results when: always command: | - docker cp testcontainer:/go/src/github.com/hashicorp/vault/test-results . + docker cp testcontainer:/home/circleci/go/src/github.com/hashicorp/vault/test-results . docker cp testcontainer:/tmp/gocache << parameters.cache_dir >> - when: condition: << parameters.save_cache >> diff --git a/.circleci/config/commands/setup-go.yml b/.circleci/config/commands/setup-go.yml index b7c515910517..a06d2f1099f3 100644 --- a/.circleci/config/commands/setup-go.yml +++ b/.circleci/config/commands/setup-go.yml @@ -22,7 +22,7 @@ steps: sudo rm -rf /usr/local/go sudo tar -C /usr/local -xzf "go${GO_VERSION}.linux-amd64.tar.gz" rm -f "go${GO_VERSION}.linux-amd64.tar.gz" - GOPATH="/go" + GOPATH="/home/circleci/go" mkdir $GOPATH 2>/dev/null || { sudo mkdir $GOPATH && sudo chmod 777 $GOPATH; } echo "export GOPATH='$GOPATH'" >> "$BASH_ENV" echo "export PATH='$PATH:$GOPATH/bin:/usr/local/go/bin'" >> "$BASH_ENV" diff --git a/.circleci/config/executors/@executors.yml b/.circleci/config/executors/@executors.yml index 5cf5d8fcf879..092349046de1 100644 --- a/.circleci/config/executors/@executors.yml +++ b/.circleci/config/executors/@executors.yml @@ -3,46 +3,46 @@ go-machine: shell: /usr/bin/env bash -euo pipefail -c environment: CIRCLECI_CLI_VERSION: 0.1.5546 # Pin CircleCI CLI to patch version (ex: 1.2.3) - GO_VERSION: 1.17.2 # Pin Go to patch version (ex: 1.2.3) + GO_VERSION: 1.17.5 # Pin Go to patch version (ex: 1.2.3) GOTESTSUM_VERSION: 0.5.2 # Pin gotestsum to patch version (ex: 1.2.3) GO_TAGS: "" - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault node: docker: - image: docker.mirror.hashicorp.services/node:14-buster shell: /usr/bin/env bash -euo pipefail -c - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault python: docker: - image: docker.mirror.hashicorp.services/python:3-alpine shell: /usr/bin/env bash -euo pipefail -c - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault alpine: docker: - image: docker.mirror.hashicorp.services/alpine:3.13 shell: /bin/sh - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault docker-env-go-test-remote-docker: resource_class: medium docker: - - image: "docker.mirror.hashicorp.services/circleci/golang:1.17.2-buster" + - image: "docker.mirror.hashicorp.services/cimg/go:1.17.5" environment: CIRCLECI_CLI_VERSION: 0.1.5546 # Pin CircleCI CLI to patch version (ex: 1.2.3) GO_TAGS: "" - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault docker-env-go-test: resource_class: large docker: - - image: "docker.mirror.hashicorp.services/circleci/golang:1.17.2-buster" + - image: "docker.mirror.hashicorp.services/cimg/go:1.17.5" environment: CIRCLECI_CLI_VERSION: 0.1.5546 # Pin CircleCI CLI to patch version (ex: 1.2.3) GO_TAGS: "" - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault docker-env-go-test-race: resource_class: xlarge docker: - - image: "docker.mirror.hashicorp.services/circleci/golang:1.17.2-buster" + - image: "docker.mirror.hashicorp.services/cimg/go:1.17.5" environment: CIRCLECI_CLI_VERSION: 0.1.5546 # Pin CircleCI CLI to patch version (ex: 1.2.3) GO_TAGS: "" - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b369521d202d..7132605cdd78 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -50,7 +50,7 @@ jobs: matrix: goos: [ freebsd, windows, netbsd, openbsd, solaris ] goarch: [ "386", "amd64", "arm" ] - go: [ "1.17.2" ] + go: [ "1.17.5" ] exclude: - goos: solaris goarch: 386 @@ -102,7 +102,7 @@ jobs: matrix: goos: [linux] goarch: ["arm", "arm64", "386", "amd64"] - go: ["1.17.2"] + go: ["1.17.5"] fail-fast: true name: Go ${{ matrix.go }} ${{ matrix.goos }} ${{ matrix.goarch }} build @@ -179,7 +179,7 @@ jobs: matrix: goos: [ darwin ] goarch: [ "amd64", "arm64" ] - go: [ "1.17.2" ] + go: [ "1.17.5" ] fail-fast: true name: Go ${{ matrix.go }} ${{ matrix.goos }} ${{ matrix.goarch }} build steps: @@ -239,4 +239,4 @@ jobs: zip_artifact_name: ${{ env.PKG_NAME }}_${{ needs.get-product-version.outputs.product-version }}_linux_${{ matrix.arch }}.zip tags: | docker.io/hashicorp/${{env.repo}}:${{env.version}} - ecr.public.aws/hashicorp/${{env.repo}}:${{env.version}} + public.ecr.aws/hashicorp/${{env.repo}}:${{env.version}} diff --git a/CHANGELOG.md b/CHANGELOG.md index f6ea78fcece1..cc1e3a21a171 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,34 @@ +## 1.10.0 +### Unreleased + +Changelog preview coming soon! + +## 1.9.1 +### December 9, 2021 + +IMPROVEMENTS: + +* storage/aerospike: Upgrade `aerospike-client-go` to v5.6.0. [[GH-12165](https://github.com/hashicorp/vault/pull/12165)] + +BUG FIXES: + +* auth/approle: Fix regression where unset cidrlist is returned as nil instead of zero-length array. [[GH-13235](https://github.com/hashicorp/vault/pull/13235)] +* ha (enterprise): Prevents performance standby nodes from serving and caching stale data immediately after performance standby election completes +* http:Fix /sys/monitor endpoint returning streaming not supported [[GH-13200](https://github.com/hashicorp/vault/pull/13200)] +* identity/oidc: Make the `nonce` parameter optional for the Authorization Endpoint of OIDC providers. [[GH-13231](https://github.com/hashicorp/vault/pull/13231)] +* identity: Fixes a panic in the OIDC key rotation due to a missing nil check. [[GH-13298](https://github.com/hashicorp/vault/pull/13298)] +* sdk/queue: move lock before length check to prevent panics. [[GH-13146](https://github.com/hashicorp/vault/pull/13146)] +* secrets/azure: Fixes service principal generation when assigning roles that have [DataActions](https://docs.microsoft.com/en-us/azure/role-based-access-control/role-definitions#dataactions). [[GH-13277](https://github.com/hashicorp/vault/pull/13277)] +* secrets/pki: Recognize ed25519 when requesting a response in PKCS8 format [[GH-13257](https://github.com/hashicorp/vault/pull/13257)] +* storage/raft: Fix a panic when trying to store a key > 32KB in a transaction. [[GH-13286](https://github.com/hashicorp/vault/pull/13286)] +* storage/raft: Fix a panic when trying to write a key > 32KB [[GH-13282](https://github.com/hashicorp/vault/pull/13282)] +* ui: Do not show verify connection value on database connection config page [[GH-13152](https://github.com/hashicorp/vault/pull/13152)] +* ui: Fixes issue restoring raft storage snapshot [[GH-13107](https://github.com/hashicorp/vault/pull/13107)] +* ui: Fixes issue with OIDC auth workflow when using MetaMask Chrome extension [[GH-13133](https://github.com/hashicorp/vault/pull/13133)] +* ui: Fixes issue with automate secret deletion value not displaying initially if set in secret metadata edit view [[GH-13177](https://github.com/hashicorp/vault/pull/13177)] +* ui: Fixes issue with placeholder not displaying for automatically deleted secrets when deletion time has passed [[GH-13166](https://github.com/hashicorp/vault/pull/13166)] +* ui: Fixes node-forge error when parsing EC (elliptical curve) certs [[GH-13238](https://github.com/hashicorp/vault/pull/13238)] + ## 1.9.0 ### November 17, 2021 @@ -198,6 +229,24 @@ of dirty pages in the merkle tree at time of checkpoint creation. [[GH-2093](htt * ui: update bar chart when model changes [[GH-12622](https://github.com/hashicorp/vault/pull/12622)] * ui: updating database TTL picker help text. [[GH-12212](https://github.com/hashicorp/vault/pull/12212)] +## 1.8.6 +### December 9, 2021 + +CHANGES: + +* go: Update go version to 1.16.9 [[GH-13029](https://github.com/hashicorp/vault/pull/13029)] + +BUG FIXES: + +* ha (enterprise): Prevents performance standby nodes from serving and caching stale data immediately after performance standby election completes +* storage/raft: Fix a panic when trying to store a key > 32KB in a transaction. [[GH-13286](https://github.com/hashicorp/vault/pull/13286)] +* storage/raft: Fix a panic when trying to write a key > 32KB [[GH-13282](https://github.com/hashicorp/vault/pull/13282)] +* ui: Adds pagination to auth methods list view [[GH-13054](https://github.com/hashicorp/vault/pull/13054)] +* ui: Do not show verify connection value on database connection config page [[GH-13152](https://github.com/hashicorp/vault/pull/13152)] +* ui: Fixes issue restoring raft storage snapshot [[GH-13107](https://github.com/hashicorp/vault/pull/13107)] +* ui: Fixes issue with OIDC auth workflow when using MetaMask Chrome extension [[GH-13133](https://github.com/hashicorp/vault/pull/13133)] +* ui: Fixes issue with the number of PGP Key inputs not matching the key shares number in the initialization form on change [[GH-13038](https://github.com/hashicorp/vault/pull/13038)] + ## 1.8.5 ### November 4, 2021 @@ -459,6 +508,18 @@ BUG FIXES: * ui: fix issue where select-one option was not showing in secrets database role creation [[GH-11294](https://github.com/hashicorp/vault/pull/11294)] * ui: fix oidc login with Safari [[GH-11884](https://github.com/hashicorp/vault/pull/11884)] +## 1.7.7 +### December 9, 2021 + +BUG FIXES: + +* ha (enterprise): Prevents performance standby nodes from serving and caching stale data immediately after performance standby election completes +* storage/raft: Fix a panic when trying to store a key > 32KB in a transaction. [[GH-13286](https://github.com/hashicorp/vault/pull/13286)] +* storage/raft: Fix a panic when trying to write a key > 32KB [[GH-13282](https://github.com/hashicorp/vault/pull/13282)] +* ui: Fixes issue restoring raft storage snapshot [[GH-13107](https://github.com/hashicorp/vault/pull/13107)] +* ui: Fixes issue with OIDC auth workflow when using MetaMask Chrome extension [[GH-13133](https://github.com/hashicorp/vault/pull/13133)] +* ui: Fixes issue with the number of PGP Key inputs not matching the key shares number in the initialization form on change [[GH-13038](https://github.com/hashicorp/vault/pull/13038)] + ## 1.7.6 ### November 4, 2021 diff --git a/Makefile b/Makefile index 233b081ea49c..01a50c82a079 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ EXTERNAL_TOOLS=\ GOFMT_FILES?=$$(find . -name '*.go' | grep -v pb.go | grep -v vendor) -GO_VERSION_MIN=1.17.2 +GO_VERSION_MIN=1.17.5 GO_CMD?=go CGO_ENABLED?=0 ifneq ($(FDB_ENABLED), ) diff --git a/README.md b/README.md index a871940d1c21..14c85eb77226 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ Developing Vault If you wish to work on Vault itself or any of its built-in systems, you'll first need [Go](https://www.golang.org) installed on your machine. Go version -1.17.2+ is *required*. +1.17.5+ is *required*. For local dev first make sure Go is properly installed, including setting up a [GOPATH](https://golang.org/doc/code.html#GOPATH). Ensure that `$GOPATH/bin` is in diff --git a/builtin/credential/github/backend_test.go b/builtin/credential/github/backend_test.go index 4e51eedba855..f3360f52cfb5 100644 --- a/builtin/credential/github/backend_test.go +++ b/builtin/credential/github/backend_test.go @@ -2,6 +2,7 @@ package github import ( "context" + "errors" "fmt" "os" "strings" @@ -70,6 +71,9 @@ func testLoginWrite(t *testing.T, d map[string]interface{}, expectedTTL time.Dur ErrorOk: true, Data: d, Check: func(resp *logical.Response) error { + if resp == nil { + return errors.New("expected a response but got nil") + } if resp.IsError() && expectFail { return nil } diff --git a/builtin/credential/github/path_config.go b/builtin/credential/github/path_config.go index 54b2b3d9d097..84c03d3dbb79 100644 --- a/builtin/credential/github/path_config.go +++ b/builtin/credential/github/path_config.go @@ -7,6 +7,7 @@ import ( "strings" "time" + "github.com/google/go-github/github" "github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/helper/tokenutil" "github.com/hashicorp/vault/sdk/logical" @@ -19,8 +20,12 @@ func pathConfig(b *backend) *framework.Path { "organization": { Type: framework.TypeString, Description: "The organization users must be part of", + Required: true, + }, + "organization_id": { + Type: framework.TypeInt64, + Description: "The ID of the organization users must be part of", }, - "base_url": { Type: framework.TypeString, Description: `The API endpoint to use. Useful if you @@ -55,6 +60,7 @@ API-compatible authentication server.`, } func (b *backend) pathConfigWrite(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { + var resp logical.Response c, err := b.Config(ctx, req.Storage) if err != nil { return nil, err @@ -66,19 +72,47 @@ func (b *backend) pathConfigWrite(ctx context.Context, req *logical.Request, dat if organizationRaw, ok := data.GetOk("organization"); ok { c.Organization = organizationRaw.(string) } + if c.Organization == "" { + return logical.ErrorResponse("organization is a required parameter"), nil + } + if organizationRaw, ok := data.GetOk("organization_id"); ok { + c.OrganizationID = organizationRaw.(int64) + } + + var parsedURL *url.URL if baseURLRaw, ok := data.GetOk("base_url"); ok { baseURL := baseURLRaw.(string) - _, err := url.Parse(baseURL) - if err != nil { - return logical.ErrorResponse(fmt.Sprintf("Error parsing given base_url: %s", err)), nil - } if !strings.HasSuffix(baseURL, "/") { baseURL += "/" } + parsedURL, err = url.Parse(baseURL) + if err != nil { + return logical.ErrorResponse(fmt.Sprintf("error parsing given base_url: %s", err)), nil + } c.BaseURL = baseURL } + if c.OrganizationID == 0 { + client, err := b.Client("") + if err != nil { + return nil, err + } + // ensure our client has the BaseURL if it was provided + if parsedURL != nil { + client.BaseURL = parsedURL + } + + // we want to set the Org ID in the config so we can use that to verify + // the credentials on login + err = c.setOrganizationID(ctx, client) + if err != nil { + errorMsg := fmt.Errorf("unable to fetch the organization_id, you must manually set it in the config: %s", err) + b.Logger().Error(errorMsg.Error()) + return nil, errorMsg + } + } + if err := c.ParseTokenFields(req, data); err != nil { return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest } @@ -103,7 +137,11 @@ func (b *backend) pathConfigWrite(ctx context.Context, req *logical.Request, dat return nil, err } - return nil, nil + if len(resp.Warnings) == 0 { + return nil, nil + } + + return &resp, nil } func (b *backend) pathConfigRead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { @@ -116,8 +154,9 @@ func (b *backend) pathConfigRead(ctx context.Context, req *logical.Request, data } d := map[string]interface{}{ - "organization": config.Organization, - "base_url": config.BaseURL, + "organization_id": config.OrganizationID, + "organization": config.Organization, + "base_url": config.BaseURL, } config.PopulateTokenData(d) @@ -163,8 +202,25 @@ func (b *backend) Config(ctx context.Context, s logical.Storage) (*config, error type config struct { tokenutil.TokenParams - Organization string `json:"organization" structs:"organization" mapstructure:"organization"` - BaseURL string `json:"base_url" structs:"base_url" mapstructure:"base_url"` - TTL time.Duration `json:"ttl" structs:"ttl" mapstructure:"ttl"` - MaxTTL time.Duration `json:"max_ttl" structs:"max_ttl" mapstructure:"max_ttl"` + OrganizationID int64 `json:"organization_id" structs:"organization_id" mapstructure:"organization_id"` + Organization string `json:"organization" structs:"organization" mapstructure:"organization"` + BaseURL string `json:"base_url" structs:"base_url" mapstructure:"base_url"` + TTL time.Duration `json:"ttl" structs:"ttl" mapstructure:"ttl"` + MaxTTL time.Duration `json:"max_ttl" structs:"max_ttl" mapstructure:"max_ttl"` +} + +func (c *config) setOrganizationID(ctx context.Context, client *github.Client) error { + org, _, err := client.Organizations.Get(ctx, c.Organization) + if err != nil { + return err + } + + orgID := org.GetID() + if orgID == 0 { + return fmt.Errorf("organization_id not found for %s", c.Organization) + } + + c.OrganizationID = orgID + + return nil } diff --git a/builtin/credential/github/path_config_test.go b/builtin/credential/github/path_config_test.go new file mode 100644 index 000000000000..e8d0cf5fdb39 --- /dev/null +++ b/builtin/credential/github/path_config_test.go @@ -0,0 +1,214 @@ +package github + +import ( + "context" + "errors" + "fmt" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/hashicorp/vault/sdk/logical" + "github.com/stretchr/testify/assert" +) + +func createBackendWithStorage(t *testing.T) (*backend, logical.Storage) { + t.Helper() + config := logical.TestBackendConfig() + config.StorageView = &logical.InmemStorage{} + + b := Backend() + if b == nil { + t.Fatalf("failed to create backend") + } + err := b.Backend.Setup(context.Background(), config) + if err != nil { + t.Fatal(err) + } + return b, config.StorageView +} + +// setupTestServer configures httptest server to intercept and respond to the +// request to base_url +func setupTestServer(t *testing.T) *httptest.Server { + t.Helper() + return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var resp string + if strings.Contains(r.URL.String(), "/user/orgs") { + resp = string(listOrgResponse) + } else if strings.Contains(r.URL.String(), "/user/teams") { + resp = string(listUserTeamsResponse) + } else if strings.Contains(r.URL.String(), "/user") { + resp = getUserResponse + } else if strings.Contains(r.URL.String(), "/orgs/") { + resp = getOrgResponse + } + + w.Header().Add("Content-Type", "application/json") + fmt.Fprintln(w, resp) + })) +} + +// TestGitHub_WriteReadConfig tests that we can successfully read and write +// the github auth config +func TestGitHub_WriteReadConfig(t *testing.T) { + b, s := createBackendWithStorage(t) + + // use a test server to return our mock GH org info + ts := setupTestServer(t) + defer ts.Close() + + // Write the config + resp, err := b.HandleRequest(context.Background(), &logical.Request{ + Path: "config", + Operation: logical.UpdateOperation, + Data: map[string]interface{}{ + "organization": "foo-org", + "base_url": ts.URL, // base_url will call the test server + }, + Storage: s, + }) + assert.NoError(t, err) + assert.Nil(t, resp) + assert.NoError(t, resp.Error()) + + // Read the config + resp, err = b.HandleRequest(context.Background(), &logical.Request{ + Path: "config", + Operation: logical.ReadOperation, + Storage: s, + }) + assert.NoError(t, err) + assert.NoError(t, resp.Error()) + + // the ID should be set, we grab it from the GET /orgs API + assert.Equal(t, int64(12345), resp.Data["organization_id"]) + assert.Equal(t, "foo-org", resp.Data["organization"]) +} + +// TestGitHub_WriteReadConfig_OrgID tests that we can successfully read and +// write the github auth config with an organization_id param +func TestGitHub_WriteReadConfig_OrgID(t *testing.T) { + b, s := createBackendWithStorage(t) + + // Write the config and pass in organization_id + resp, err := b.HandleRequest(context.Background(), &logical.Request{ + Path: "config", + Operation: logical.UpdateOperation, + Data: map[string]interface{}{ + "organization": "foo-org", + "organization_id": 98765, + }, + Storage: s, + }) + assert.NoError(t, err) + assert.Nil(t, resp) + assert.NoError(t, resp.Error()) + + // Read the config + resp, err = b.HandleRequest(context.Background(), &logical.Request{ + Path: "config", + Operation: logical.ReadOperation, + Storage: s, + }) + assert.NoError(t, err) + assert.NoError(t, resp.Error()) + + // the ID should be set to what was written in the config + assert.Equal(t, int64(98765), resp.Data["organization_id"]) + assert.Equal(t, "foo-org", resp.Data["organization"]) +} + +// TestGitHub_ErrorNoOrgID tests that an error is returned when we cannot fetch +// the org ID for the given org name +func TestGitHub_ErrorNoOrgID(t *testing.T) { + b, s := createBackendWithStorage(t) + // use a test server to return our mock GH org info + ts := func() *httptest.Server { + return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Content-Type", "application/json") + resp := `{ "id": 0 }` + fmt.Fprintln(w, resp) + })) + } + + defer ts().Close() + + // Write the config + resp, err := b.HandleRequest(context.Background(), &logical.Request{ + Path: "config", + Operation: logical.UpdateOperation, + Data: map[string]interface{}{ + "organization": "foo-org", + "base_url": ts().URL, // base_url will call the test server + }, + Storage: s, + }) + assert.Error(t, err) + assert.Nil(t, resp) + assert.Equal(t, errors.New( + "unable to fetch the organization_id, you must manually set it in the config: organization_id not found for foo-org", + ), err) +} + +// TestGitHub_WriteConfig_ErrorNoOrg tests that an error is returned when the +// required "organization" parameter is not provided +func TestGitHub_WriteConfig_ErrorNoOrg(t *testing.T) { + b, s := createBackendWithStorage(t) + + // Write the config + resp, err := b.HandleRequest(context.Background(), &logical.Request{ + Path: "config", + Operation: logical.UpdateOperation, + Data: map[string]interface{}{}, + Storage: s, + }) + + assert.NoError(t, err) + assert.Error(t, resp.Error()) + assert.Equal(t, errors.New("organization is a required parameter"), resp.Error()) +} + +// https://docs.github.com/en/rest/reference/users#get-the-authenticated-user +// Note: many of the fields have been omitted +var getUserResponse = ` +{ + "login": "user-foo", + "id": 6789, + "description": "A great user. The very best user.", + "name": "foo name", + "company": "foo-company", + "type": "User" +} +` + +// https://docs.github.com/en/rest/reference/orgs#get-an-organization +// Note: many of the fields have been omitted, we only care about 'login' and 'id' +var getOrgResponse = ` +{ + "login": "foo-org", + "id": 12345, + "description": "A great org. The very best org.", + "name": "foo-display-name", + "company": "foo-company", + "type": "Organization" +} +` + +// https://docs.github.com/en/rest/reference/orgs#list-organizations-for-the-authenticated-user +var listOrgResponse = []byte(fmt.Sprintf(`[%v]`, getOrgResponse)) + +// https://docs.github.com/en/rest/reference/teams#list-teams-for-the-authenticated-user +// Note: many of the fields have been omitted +var listUserTeamsResponse = []byte(fmt.Sprintf(`[ +{ + "id": 1, + "node_id": "MDQ6VGVhbTE=", + "name": "Foo team", + "slug": "foo-team", + "description": "A great team. The very best team.", + "permission": "admin", + "organization": %v + } +]`, getOrgResponse)) diff --git a/builtin/credential/github/path_login.go b/builtin/credential/github/path_login.go index 68070e4829bd..252b5641cd4f 100644 --- a/builtin/credential/github/path_login.go +++ b/builtin/credential/github/path_login.go @@ -2,9 +2,9 @@ package github import ( "context" + "errors" "fmt" "net/url" - "strings" "github.com/google/go-github/github" "github.com/hashicorp/vault/sdk/framework" @@ -33,16 +33,13 @@ func pathLogin(b *backend) *framework.Path { func (b *backend) pathLoginAliasLookahead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { token := data.Get("token").(string) - var verifyResp *verifyCredentialsResp - if verifyResponse, resp, err := b.verifyCredentials(ctx, req, token); err != nil { + verifyResp, err := b.verifyCredentials(ctx, req, token) + if err != nil { return nil, err - } else if resp != nil { - return resp, nil - } else { - verifyResp = verifyResponse } return &logical.Response{ + Warnings: verifyResp.Warnings, Auth: &logical.Auth{ Alias: &logical.Alias{ Name: *verifyResp.User.Login, @@ -54,13 +51,9 @@ func (b *backend) pathLoginAliasLookahead(ctx context.Context, req *logical.Requ func (b *backend) pathLogin(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { token := data.Get("token").(string) - var verifyResp *verifyCredentialsResp - if verifyResponse, resp, err := b.verifyCredentials(ctx, req, token); err != nil { + verifyResp, err := b.verifyCredentials(ctx, req, token) + if err != nil { return nil, err - } else if resp != nil { - return resp, nil - } else { - verifyResp = verifyResponse } auth := &logical.Auth{ @@ -84,7 +77,8 @@ func (b *backend) pathLogin(ctx context.Context, req *logical.Request, data *fra } resp := &logical.Response{ - Auth: auth, + Warnings: verifyResp.Warnings, + Auth: auth, } for _, teamName := range verifyResp.TeamNames { @@ -110,14 +104,11 @@ func (b *backend) pathLoginRenew(ctx context.Context, req *logical.Request, d *f } token := tokenRaw.(string) - var verifyResp *verifyCredentialsResp - if verifyResponse, resp, err := b.verifyCredentials(ctx, req, token); err != nil { + verifyResp, err := b.verifyCredentials(ctx, req, token) + if err != nil { return nil, err - } else if resp != nil { - return resp, nil - } else { - verifyResp = verifyResponse } + if !policyutil.EquivalentPolicies(verifyResp.Policies, req.Auth.TokenPolicies) { return nil, fmt.Errorf("policies do not match") } @@ -126,6 +117,7 @@ func (b *backend) pathLoginRenew(ctx context.Context, req *logical.Request, d *f resp.Auth.Period = verifyResp.Config.TokenPeriod resp.Auth.TTL = verifyResp.Config.TokenTTL resp.Auth.MaxTTL = verifyResp.Config.TokenMaxTTL + resp.Warnings = verifyResp.Warnings // Remove old aliases resp.Auth.GroupAliases = nil @@ -139,48 +131,64 @@ func (b *backend) pathLoginRenew(ctx context.Context, req *logical.Request, d *f return resp, nil } -func (b *backend) verifyCredentials(ctx context.Context, req *logical.Request, token string) (*verifyCredentialsResp, *logical.Response, error) { +func (b *backend) verifyCredentials(ctx context.Context, req *logical.Request, token string) (*verifyCredentialsResp, error) { + var warnings []string config, err := b.Config(ctx, req.Storage) if err != nil { - return nil, nil, err + return nil, err } if config == nil { - return nil, logical.ErrorResponse("configuration has not been set"), nil + return nil, errors.New("configuration has not been set") } // Check for a CIDR match. if len(config.TokenBoundCIDRs) > 0 { if req.Connection == nil { - b.Logger().Warn("token bound CIDRs found but no connection information available for validation") - return nil, nil, logical.ErrPermissionDenied + b.Logger().Error("token bound CIDRs found but no connection information available for validation") + return nil, logical.ErrPermissionDenied } if !cidrutil.RemoteAddrIsOk(req.Connection.RemoteAddr, config.TokenBoundCIDRs) { - return nil, nil, logical.ErrPermissionDenied + return nil, logical.ErrPermissionDenied } } - if config.Organization == "" { - return nil, logical.ErrorResponse( - "organization not found in configuration"), nil - } - client, err := b.Client(token) if err != nil { - return nil, nil, err + return nil, err } if config.BaseURL != "" { parsedURL, err := url.Parse(config.BaseURL) if err != nil { - return nil, nil, fmt.Errorf("successfully parsed base_url when set but failing to parse now: %w", err) + return nil, fmt.Errorf("successfully parsed base_url when set but failing to parse now: %w", err) } client.BaseURL = parsedURL } + if config.OrganizationID == 0 { + // Previously we did not verify using the Org ID. So if the Org ID is + // not set, we will trust-on-first-use and set it now. + err = config.setOrganizationID(ctx, client) + if err != nil { + b.Logger().Error("failed to set the organization_id on login", "error", err) + return nil, err + } + entry, err := logical.StorageEntryJSON("config", config) + if err != nil { + return nil, err + } + + if err := req.Storage.Put(ctx, entry); err != nil { + return nil, err + } + + b.Logger().Info("set ID on a trust-on-first-use basis", "organization_id", config.OrganizationID) + } + // Get the user user, _, err := client.Users.Get(ctx, "") if err != nil { - return nil, nil, err + return nil, err } // Verify that the user is part of the organization @@ -194,7 +202,7 @@ func (b *backend) verifyCredentials(ctx context.Context, req *logical.Request, t for { orgs, resp, err := client.Organizations.List(ctx, "", orgOpt) if err != nil { - return nil, nil, err + return nil, err } allOrgs = append(allOrgs, orgs...) if resp.NextPage == 0 { @@ -203,14 +211,27 @@ func (b *backend) verifyCredentials(ctx context.Context, req *logical.Request, t orgOpt.Page = resp.NextPage } + orgLoginName := "" for _, o := range allOrgs { - if strings.EqualFold(*o.Login, config.Organization) { + if o.GetID() == config.OrganizationID { org = o + orgLoginName = *o.Login break } } if org == nil { - return nil, logical.ErrorResponse("user is not part of required org"), nil + return nil, errors.New("user is not part of required org") + } + + if orgLoginName != config.Organization { + warningMsg := fmt.Sprintf( + "the organization name has changed to %q. It is recommended to verify and update the organization name in the config: %s=%d", + orgLoginName, + "organization_id", + config.OrganizationID, + ) + b.Logger().Warn(warningMsg) + warnings = append(warnings, warningMsg) } // Get the teams that this user is part of to determine the policies @@ -224,7 +245,7 @@ func (b *backend) verifyCredentials(ctx context.Context, req *logical.Request, t for { teams, resp, err := client.Teams.ListUserTeams(ctx, teamOpt) if err != nil { - return nil, nil, err + return nil, err } allTeams = append(allTeams, teams...) if resp.NextPage == 0 { @@ -248,21 +269,24 @@ func (b *backend) verifyCredentials(ctx context.Context, req *logical.Request, t groupPoliciesList, err := b.TeamMap.Policies(ctx, req.Storage, teamNames...) if err != nil { - return nil, nil, err + return nil, err } userPoliciesList, err := b.UserMap.Policies(ctx, req.Storage, []string{*user.Login}...) if err != nil { - return nil, nil, err + return nil, err } - return &verifyCredentialsResp{ + verifyResp := &verifyCredentialsResp{ User: user, Org: org, Policies: append(groupPoliciesList, userPoliciesList...), TeamNames: teamNames, Config: config, - }, nil, nil + Warnings: warnings, + } + + return verifyResp, nil } type verifyCredentialsResp struct { @@ -271,6 +295,9 @@ type verifyCredentialsResp struct { Policies []string TeamNames []string + // Warnings to send back to the caller + Warnings []string + // This is just a cache to send back to the caller Config *config } diff --git a/builtin/credential/github/path_login_test.go b/builtin/credential/github/path_login_test.go new file mode 100644 index 000000000000..25baf7f811e8 --- /dev/null +++ b/builtin/credential/github/path_login_test.go @@ -0,0 +1,186 @@ +package github + +import ( + "context" + "errors" + "testing" + + "github.com/hashicorp/vault/helper/namespace" + "github.com/hashicorp/vault/sdk/logical" + "github.com/stretchr/testify/assert" +) + +// TestGitHub_Login tests that we can successfully login with the given config +func TestGitHub_Login(t *testing.T) { + b, s := createBackendWithStorage(t) + + // use a test server to return our mock GH org info + ts := setupTestServer(t) + defer ts.Close() + + // Write the config + resp, err := b.HandleRequest(context.Background(), &logical.Request{ + Path: "config", + Operation: logical.UpdateOperation, + Data: map[string]interface{}{ + "organization": "foo-org", + "base_url": ts.URL, // base_url will call the test server + }, + Storage: s, + }) + assert.NoError(t, err) + assert.NoError(t, resp.Error()) + + // Read the config + resp, err = b.HandleRequest(context.Background(), &logical.Request{ + Path: "config", + Operation: logical.ReadOperation, + Storage: s, + }) + assert.NoError(t, err) + assert.NoError(t, resp.Error()) + + // attempt a login + resp, err = b.HandleRequest(context.Background(), &logical.Request{ + Path: "login", + Operation: logical.UpdateOperation, + Storage: s, + }) + + expectedMetaData := map[string]string{ + "org": "foo-org", + "username": "user-foo", + } + assert.Equal(t, expectedMetaData, resp.Auth.Metadata) + assert.NoError(t, err) + assert.NoError(t, resp.Error()) +} + +// TestGitHub_Login_OrgInvalid tests that we cannot login with an ID other than +// what is set in the config +func TestGitHub_Login_OrgInvalid(t *testing.T) { + b, s := createBackendWithStorage(t) + ctx := namespace.RootContext(nil) + + // use a test server to return our mock GH org info + ts := setupTestServer(t) + defer ts.Close() + + // write and store config + config := config{ + Organization: "foo-org", + OrganizationID: 9999, + BaseURL: ts.URL + "/", // base_url will call the test server + } + entry, err := logical.StorageEntryJSON("config", config) + if err != nil { + t.Fatalf("failed creating storage entry") + } + if err := s.Put(ctx, entry); err != nil { + t.Fatalf("writing to in mem storage failed") + } + + // attempt a login + resp, err := b.HandleRequest(context.Background(), &logical.Request{ + Path: "login", + Operation: logical.UpdateOperation, + Storage: s, + }) + + assert.Nil(t, resp) + assert.Error(t, err) + assert.Equal(t, errors.New("user is not part of required org"), err) +} + +// TestGitHub_Login_OrgNameChanged tests that we can successfully login with the +// given config and emit a warning when the organization name has changed +func TestGitHub_Login_OrgNameChanged(t *testing.T) { + b, s := createBackendWithStorage(t) + ctx := namespace.RootContext(nil) + + // use a test server to return our mock GH org info + ts := setupTestServer(t) + defer ts.Close() + + // write and store config + // the name does not match what the API will return but the ID does + config := config{ + Organization: "old-name", + OrganizationID: 12345, + BaseURL: ts.URL + "/", // base_url will call the test server + } + entry, err := logical.StorageEntryJSON("config", config) + if err != nil { + t.Fatalf("failed creating storage entry") + } + if err := s.Put(ctx, entry); err != nil { + t.Fatalf("writing to in mem storage failed") + } + + // attempt a login + resp, err := b.HandleRequest(context.Background(), &logical.Request{ + Path: "login", + Operation: logical.UpdateOperation, + Storage: s, + }) + + assert.NoError(t, err) + assert.Nil(t, resp.Error()) + assert.Equal( + t, + []string{"the organization name has changed to \"foo-org\". It is recommended to verify and update the organization name in the config: organization_id=12345"}, + resp.Warnings, + ) +} + +// TestGitHub_Login_NoOrgID tests that we can successfully login with the given +// config when no organization ID is present and write the fetched ID to the +// config +func TestGitHub_Login_NoOrgID(t *testing.T) { + b, s := createBackendWithStorage(t) + ctx := namespace.RootContext(nil) + + // use a test server to return our mock GH org info + ts := setupTestServer(t) + defer ts.Close() + + // write and store config without Org ID + config := config{ + Organization: "foo-org", + BaseURL: ts.URL + "/", // base_url will call the test server + } + entry, err := logical.StorageEntryJSON("config", config) + if err != nil { + t.Fatalf("failed creating storage entry") + } + if err := s.Put(ctx, entry); err != nil { + t.Fatalf("writing to in mem storage failed") + } + + // attempt a login + resp, err := b.HandleRequest(context.Background(), &logical.Request{ + Path: "login", + Operation: logical.UpdateOperation, + Storage: s, + }) + + expectedMetaData := map[string]string{ + "org": "foo-org", + "username": "user-foo", + } + assert.Equal(t, expectedMetaData, resp.Auth.Metadata) + assert.NoError(t, err) + assert.NoError(t, resp.Error()) + + // Read the config + resp, err = b.HandleRequest(context.Background(), &logical.Request{ + Path: "config", + Operation: logical.ReadOperation, + Storage: s, + }) + assert.NoError(t, err) + assert.NoError(t, resp.Error()) + + // the ID should be set, we grab it from the GET /orgs API + assert.Equal(t, int64(12345), resp.Data["organization_id"]) +} diff --git a/builtin/logical/pki/backend_test.go b/builtin/logical/pki/backend_test.go index 17dd6c11b2d1..995bea5982d4 100644 --- a/builtin/logical/pki/backend_test.go +++ b/builtin/logical/pki/backend_test.go @@ -1183,21 +1183,21 @@ func generateRoleSteps(t *testing.T, useCSRs bool) []logicaltest.TestStep { } getRandCsr := func(keyType string, errorOk bool, csrTemplate *x509.CertificateRequest) csrPlan { - rsaKeyBits := []int{2048, 4096} + rsaKeyBits := []int{2048, 3072, 4096} ecKeyBits := []int{224, 256, 384, 521} plan := csrPlan{errorOk: errorOk} var testBitSize int switch keyType { case "rsa": - plan.roleKeyBits = rsaKeyBits[mathRand.Int()%2] + plan.roleKeyBits = rsaKeyBits[mathRand.Int()%len(rsaKeyBits)] testBitSize = plan.roleKeyBits // If we don't expect an error already, randomly choose a // key size and expect an error if it's less than the role // setting if !keybitSizeRandOff && !errorOk { - testBitSize = rsaKeyBits[mathRand.Int()%2] + testBitSize = rsaKeyBits[mathRand.Int()%len(rsaKeyBits)] } if testBitSize < plan.roleKeyBits { @@ -1205,14 +1205,14 @@ func generateRoleSteps(t *testing.T, useCSRs bool) []logicaltest.TestStep { } case "ec": - plan.roleKeyBits = ecKeyBits[mathRand.Int()%4] + plan.roleKeyBits = ecKeyBits[mathRand.Int()%len(ecKeyBits)] testBitSize = plan.roleKeyBits // If we don't expect an error already, randomly choose a // key size and expect an error if it's less than the role // setting if !keybitSizeRandOff && !errorOk { - testBitSize = ecKeyBits[mathRand.Int()%4] + testBitSize = ecKeyBits[mathRand.Int()%len(ecKeyBits)] } if testBitSize < plan.roleKeyBits { diff --git a/builtin/logical/pki/ca_util.go b/builtin/logical/pki/ca_util.go index e5009386f539..1e3da0333960 100644 --- a/builtin/logical/pki/ca_util.go +++ b/builtin/logical/pki/ca_util.go @@ -50,12 +50,8 @@ func (b *backend) getGenerationParams( PostalCode: data.Get("postal_code").([]string), } - if role.KeyType == "rsa" && role.KeyBits < 2048 { - errorResp = logical.ErrorResponse("RSA keys < 2048 bits are unsafe and not supported") - return - } - - if err := certutil.ValidateKeyTypeSignatureLength(role.KeyType, role.KeyBits, &role.SignatureBits); err != nil { + var err error + if role.KeyBits, role.SignatureBits, err = certutil.ValidateDefaultOrValueKeyTypeSignatureLength(role.KeyType, role.KeyBits, role.SignatureBits); err != nil { errorResp = logical.ErrorResponse(err.Error()) } diff --git a/builtin/logical/pki/fields.go b/builtin/logical/pki/fields.go index fcb01d07b446..67d705a505f7 100644 --- a/builtin/logical/pki/fields.go +++ b/builtin/logical/pki/fields.go @@ -250,12 +250,13 @@ the private key!`, fields["key_bits"] = &framework.FieldSchema{ Type: framework.TypeInt, - Default: 2048, - Description: `The number of bits to use. You will almost -certainly want to change this if you adjust -the key_type.`, + Default: 0, + Description: `The number of bits to use. Allowed values are +0 (universal default); with rsa key_type: 2048 (default), 3072, or +4096; with ec key_type: 224, 256 (default), 384, or 521; ignored with +ed25519.`, DisplayAttrs: &framework.DisplayAttributes{ - Value: 2048, + Value: 0, }, } diff --git a/builtin/logical/pki/path_roles.go b/builtin/logical/pki/path_roles.go index aa77dcf570fe..444e8071fdba 100644 --- a/builtin/logical/pki/path_roles.go +++ b/builtin/logical/pki/path_roles.go @@ -199,10 +199,11 @@ protection use. Defaults to false.`, "key_bits": { Type: framework.TypeInt, - Default: 2048, - Description: `The number of bits to use. You will almost -certainly want to change this if you adjust -the key_type.`, + Default: 0, + Description: `The number of bits to use. Allowed values are +0 (universal default); with rsa key_type: 2048 (default), 3072, or +4096; with ec key_type: 224, 256 (default), 384, or 521; ignored with +ed25519.`, }, "signature_bits": &framework.FieldSchema{ @@ -615,17 +616,13 @@ func (b *backend) pathRoleCreate(ctx context.Context, req *logical.Request, data *entry.GenerateLease = data.Get("generate_lease").(bool) } - if entry.KeyType == "rsa" && entry.KeyBits < 2048 { - return logical.ErrorResponse("RSA keys < 2048 bits are unsafe and not supported"), nil - } - if entry.MaxTTL > 0 && entry.TTL > entry.MaxTTL { return logical.ErrorResponse( `"ttl" value must be less than "max_ttl" value`, ), nil } - if err := certutil.ValidateKeyTypeSignatureLength(entry.KeyType, entry.KeyBits, &entry.SignatureBits); err != nil { + if entry.KeyBits, entry.SignatureBits, err = certutil.ValidateDefaultOrValueKeyTypeSignatureLength(entry.KeyType, entry.KeyBits, entry.SignatureBits); err != nil { return logical.ErrorResponse(err.Error()), nil } diff --git a/builtin/logical/transit/backend_test.go b/builtin/logical/transit/backend_test.go index bb68d9f02d5f..355b0738c1dd 100644 --- a/builtin/logical/transit/backend_test.go +++ b/builtin/logical/transit/backend_test.go @@ -989,6 +989,7 @@ func testConvergentEncryptionCommon(t *testing.T, ver int, keyType keysutil.KeyT Data: map[string]interface{}{ "derived": false, "convergent_encryption": true, + "type": keyType.String(), }, } resp, err := b.HandleRequest(context.Background(), req) @@ -1009,6 +1010,7 @@ func testConvergentEncryptionCommon(t *testing.T, ver int, keyType keysutil.KeyT Data: map[string]interface{}{ "derived": true, "convergent_encryption": true, + "type": keyType.String(), }, } resp, err = b.HandleRequest(context.Background(), req) diff --git a/builtin/logical/transit/path_datakey.go b/builtin/logical/transit/path_datakey.go index 9e9ef2c17340..a9c1f426d928 100644 --- a/builtin/logical/transit/path_datakey.go +++ b/builtin/logical/transit/path_datakey.go @@ -5,7 +5,7 @@ import ( "crypto/rand" "encoding/base64" "fmt" - + "github.com/hashicorp/vault/helper/constants" "github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/helper/errutil" "github.com/hashicorp/vault/sdk/helper/keysutil" @@ -159,6 +159,10 @@ func (b *backend) pathDatakeyWrite(ctx context.Context, req *logical.Request, d }, } + if constants.IsFIPS() && shouldWarnAboutNonceUsage(p, nonce) { + resp.AddWarning("A provided nonce value was used within FIPS mode, this violates FIPS 140 compliance.") + } + if plaintextAllowed { resp.Data["plaintext"] = base64.StdEncoding.EncodeToString(newKey) } diff --git a/builtin/logical/transit/path_encrypt.go b/builtin/logical/transit/path_encrypt.go index deb564b12926..ac683dadd17e 100644 --- a/builtin/logical/transit/path_encrypt.go +++ b/builtin/logical/transit/path_encrypt.go @@ -5,6 +5,7 @@ import ( "encoding/base64" "encoding/json" "fmt" + "github.com/hashicorp/vault/helper/constants" "net/http" "reflect" @@ -357,11 +358,16 @@ func (b *backend) pathEncryptWrite(ctx context.Context, req *logical.Request, d // Process batch request items. If encryption of any request // item fails, respectively mark the error in the response // collection and continue to process other items. + warnAboutNonceUsage := false for i, item := range batchInputItems { if batchResponseItems[i].Error != "" { continue } + if !warnAboutNonceUsage && shouldWarnAboutNonceUsage(p, item.DecodedNonce) { + warnAboutNonceUsage = true + } + ciphertext, err := p.Encrypt(item.KeyVersion, item.DecodedContext, item.DecodedNonce, item.Plaintext) if err != nil { switch err.(type) { @@ -411,6 +417,10 @@ func (b *backend) pathEncryptWrite(ctx context.Context, req *logical.Request, d } } + if constants.IsFIPS() && warnAboutNonceUsage { + resp.AddWarning("A provided nonce value was used within FIPS mode, this violates FIPS 140 compliance.") + } + if req.Operation == logical.CreateOperation && !upserted { resp.AddWarning("Attempted creation of the key during the encrypt operation, but it was created beforehand") } @@ -431,6 +441,34 @@ func (b *backend) pathEncryptWrite(ctx context.Context, req *logical.Request, d return resp, nil } +// shouldWarnAboutNonceUsage attempts to determine if we will use a provided nonce or not. Ideally this +// would be information returned through p.Encrypt but that would require an SDK api change and this is +// transit specific +func shouldWarnAboutNonceUsage(p *keysutil.Policy, userSuppliedNonce []byte) bool { + if len(userSuppliedNonce) == 0 { + return false + } + + var supportedKeyType bool + switch p.Type { + case keysutil.KeyType_AES128_GCM96, keysutil.KeyType_AES256_GCM96, keysutil.KeyType_ChaCha20_Poly1305: + supportedKeyType = true + default: + supportedKeyType = false + } + + if supportedKeyType && p.ConvergentEncryption && p.ConvergentVersion == 1 { + // We only use the user supplied nonce for v1 convergent encryption keys + return true + } + + if supportedKeyType && !p.ConvergentEncryption { + return true + } + + return false +} + const pathEncryptHelpSyn = `Encrypt a plaintext value or a batch of plaintext blocks using a named key` diff --git a/builtin/logical/transit/path_encrypt_test.go b/builtin/logical/transit/path_encrypt_test.go index 1842e069a513..227ff4cf49ca 100644 --- a/builtin/logical/transit/path_encrypt_test.go +++ b/builtin/logical/transit/path_encrypt_test.go @@ -3,6 +3,7 @@ package transit import ( "context" "encoding/json" + "github.com/hashicorp/vault/sdk/helper/keysutil" "reflect" "strings" "testing" @@ -729,3 +730,82 @@ func TestTransit_decodeBatchRequestItems(t *testing.T) { }) } } + +func TestShouldWarnAboutNonceUsage(t *testing.T) { + tests := []struct { + name string + keyTypes []keysutil.KeyType + nonce []byte + convergentEncryption bool + convergentVersion int + expected bool + }{ + { + name: "-NoConvergent-WithNonce", + keyTypes: []keysutil.KeyType{keysutil.KeyType_AES256_GCM96, keysutil.KeyType_AES128_GCM96, keysutil.KeyType_ChaCha20_Poly1305}, + nonce: []byte("testnonce"), + convergentEncryption: false, + convergentVersion: -1, + expected: true, + }, + { + name: "-NoConvergent-NoNonce", + keyTypes: []keysutil.KeyType{keysutil.KeyType_AES256_GCM96, keysutil.KeyType_AES128_GCM96, keysutil.KeyType_ChaCha20_Poly1305}, + nonce: []byte{}, + convergentEncryption: false, + convergentVersion: -1, + expected: false, + }, + { + name: "-Convergentv1-WithNonce", + keyTypes: []keysutil.KeyType{keysutil.KeyType_AES256_GCM96, keysutil.KeyType_AES128_GCM96, keysutil.KeyType_ChaCha20_Poly1305}, + nonce: []byte("testnonce"), + convergentEncryption: true, + convergentVersion: 1, + expected: true, + }, + { + name: "-Convergentv2-WithNonce", + keyTypes: []keysutil.KeyType{keysutil.KeyType_AES256_GCM96, keysutil.KeyType_AES128_GCM96, keysutil.KeyType_ChaCha20_Poly1305}, + nonce: []byte("testnonce"), + convergentEncryption: true, + convergentVersion: 2, + expected: false, + }, + { + name: "-Convergentv3-WithNonce", + keyTypes: []keysutil.KeyType{keysutil.KeyType_AES256_GCM96, keysutil.KeyType_AES128_GCM96, keysutil.KeyType_ChaCha20_Poly1305}, + nonce: []byte("testnonce"), + convergentEncryption: true, + convergentVersion: 3, + expected: false, + }, + { + name: "-NoConvergent-WithNonce", + keyTypes: []keysutil.KeyType{keysutil.KeyType_RSA2048, keysutil.KeyType_RSA4096}, + nonce: []byte("testnonce"), + convergentEncryption: false, + convergentVersion: -1, + expected: false, + }, + } + + for _, tt := range tests { + for _, keyType := range tt.keyTypes { + testName := keyType.String() + tt.name + t.Run(testName, func(t *testing.T) { + p := keysutil.Policy{ + ConvergentEncryption: tt.convergentEncryption, + ConvergentVersion: tt.convergentVersion, + Type: keyType, + } + + actual := shouldWarnAboutNonceUsage(&p, tt.nonce) + + if actual != tt.expected { + t.Errorf("Expected actual '%v' but got '%v'", tt.expected, actual) + } + }) + } + } +} diff --git a/builtin/logical/transit/path_hash.go b/builtin/logical/transit/path_hash.go index f26435d88ee9..5e4c750530db 100644 --- a/builtin/logical/transit/path_hash.go +++ b/builtin/logical/transit/path_hash.go @@ -9,6 +9,8 @@ import ( "fmt" "hash" + "golang.org/x/crypto/sha3" + "github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/logical" ) @@ -31,6 +33,10 @@ func (b *backend) pathHash() *framework.Path { * sha2-256 * sha2-384 * sha2-512 +* sha3-224 +* sha3-256 +* sha3-384 +* sha3-512 Defaults to "sha2-256".`, }, @@ -86,6 +92,14 @@ func (b *backend) pathHashWrite(ctx context.Context, req *logical.Request, d *fr hf = sha512.New384() case "sha2-512": hf = sha512.New() + case "sha3-224": + hf = sha3.New224() + case "sha3-256": + hf = sha3.New256() + case "sha3-384": + hf = sha3.New384() + case "sha3-512": + hf = sha3.New512() default: return logical.ErrorResponse(fmt.Sprintf("unsupported algorithm %s", algorithm)), nil } diff --git a/builtin/logical/transit/path_hash_test.go b/builtin/logical/transit/path_hash_test.go index a500bb2a42e4..98ce87889a72 100644 --- a/builtin/logical/transit/path_hash_test.go +++ b/builtin/logical/transit/path_hash_test.go @@ -67,6 +67,24 @@ func TestTransit_Hash(t *testing.T) { req.Data["format"] = "base64" doRequest(req, false, "2dOA8puXrWodkumH2D+loCZTMB4QBt0rzVGvpZqRR+nK7a+JUhq8DwtoKtzUf7USuDQ8g0oy8yb+m+8AVCzohw==") + // Test SHA3 + req.Data["format"] = "hex" + req.Data["algorithm"] = "sha3-224" + doRequest(req, false, "ced91e69d89c837e87cff960bd64fd9b9f92325fb9add8988d33d007") + + req.Data["algorithm"] = "sha3-256" + doRequest(req, false, "e4bd866ec3fa52df3b7842aa97b448bc859a7606cefcdad1715847f4b82a6c93") + + req.Data["algorithm"] = "sha3-384" + doRequest(req, false, "715cd38cbf8d0bab426b6a084d649760be555dd64b34de6db148a3fbf2cd2aa5d8b03eb6eda73a3e9a8769c00b4c2113") + + req.Data["algorithm"] = "sha3-512" + doRequest(req, false, "f7cac5ad830422a5408b36a60a60620687be180765a3e2895bc3bdbd857c9e08246c83064d4e3612f0cb927f3ead208413ab98624bf7b0617af0f03f62080976") + + // Test returning SHA3 as base64 + req.Data["format"] = "base64" + doRequest(req, false, "98rFrYMEIqVAizamCmBiBoe+GAdlo+KJW8O9vYV8nggkbIMGTU42EvDLkn8+rSCEE6uYYkv3sGF68PA/YggJdg==") + // Test bad input/format/algorithm req.Data["format"] = "base92" doRequest(req, true, "") diff --git a/builtin/logical/transit/path_hmac.go b/builtin/logical/transit/path_hmac.go index 30a79a40789a..f61017919c9d 100644 --- a/builtin/logical/transit/path_hmac.go +++ b/builtin/logical/transit/path_hmac.go @@ -60,6 +60,10 @@ func (b *backend) pathHMAC() *framework.Path { * sha2-256 * sha2-384 * sha2-512 +* sha3-224 +* sha3-256 +* sha3-384 +* sha3-512 Defaults to "sha2-256".`, }, diff --git a/builtin/logical/transit/path_hmac_test.go b/builtin/logical/transit/path_hmac_test.go index 756dc77e559f..108d6218bfd7 100644 --- a/builtin/logical/transit/path_hmac_test.go +++ b/builtin/logical/transit/path_hmac_test.go @@ -114,6 +114,24 @@ func TestTransit_HMAC(t *testing.T) { req.Data["format"] = "base64" doRequest(req, false, "vault:v1:PSXLXvkvKF4CpU65e2bK1tGBZQpcpCEM32fq2iUoiTyQQCfBcGJJItQ+60tMwWXAPQrC290AzTrNJucGrr4GFA==") + // Test SHA3 + req.Path = "hmac/foo" + req.Data["algorithm"] = "sha3-224" + doRequest(req, false, "vault:v1:TGipmKH8LR/BkMolYpDYy0BJCIhTtGPDhV2VkQ==") + + req.Data["algorithm"] = "sha3-256" + doRequest(req, false, "vault:v1:+px9V/7QYLfdK808zPESC2T/L33uFf4Blzsn9Jy838o=") + + req.Data["algorithm"] = "sha3-384" + doRequest(req, false, "vault:v1:YGoRwN4UdTRYZeOER86jsQOB8piWenzLDzJ2wJQK/Jq59rAsY8lh7SCdqqCyFg70") + + req.Data["algorithm"] = "sha3-512" + doRequest(req, false, "vault:v1:GrNA8sU88naMPEQ7UZGj9EJl7YJhl03AFHfxcEURFrtvnobdea9ZlZHePpxAx/oCaC7R2HkrAO+Tu3uXPIl3lg==") + + // Test returning SHA3 as base64 + req.Data["format"] = "base64" + doRequest(req, false, "vault:v1:GrNA8sU88naMPEQ7UZGj9EJl7YJhl03AFHfxcEURFrtvnobdea9ZlZHePpxAx/oCaC7R2HkrAO+Tu3uXPIl3lg==") + req.Data["algorithm"] = "foobar" doRequest(req, true, "") diff --git a/builtin/logical/transit/path_rewrap.go b/builtin/logical/transit/path_rewrap.go index c32fddc99976..04c38d2f8eb6 100644 --- a/builtin/logical/transit/path_rewrap.go +++ b/builtin/logical/transit/path_rewrap.go @@ -4,7 +4,7 @@ import ( "context" "encoding/base64" "fmt" - + "github.com/hashicorp/vault/helper/constants" "github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/helper/errutil" "github.com/hashicorp/vault/sdk/helper/keysutil" @@ -128,6 +128,7 @@ func (b *backend) pathRewrapWrite(ctx context.Context, req *logical.Request, d * p.Lock(false) } + warnAboutNonceUsage := false for i, item := range batchInputItems { if batchResponseItems[i].Error != "" { continue @@ -145,6 +146,10 @@ func (b *backend) pathRewrapWrite(ctx context.Context, req *logical.Request, d * } } + if !warnAboutNonceUsage && shouldWarnAboutNonceUsage(p, item.DecodedNonce) { + warnAboutNonceUsage = true + } + ciphertext, err := p.Encrypt(item.KeyVersion, item.DecodedContext, item.DecodedNonce, plaintext) if err != nil { switch err.(type) { @@ -190,6 +195,10 @@ func (b *backend) pathRewrapWrite(ctx context.Context, req *logical.Request, d * } } + if constants.IsFIPS() && warnAboutNonceUsage { + resp.AddWarning("A provided nonce value was used within FIPS mode, this violates FIPS 140 compliance.") + } + p.Unlock() return resp, nil } diff --git a/builtin/logical/transit/path_sign_verify.go b/builtin/logical/transit/path_sign_verify.go index 265d63cec198..ade69530df78 100644 --- a/builtin/logical/transit/path_sign_verify.go +++ b/builtin/logical/transit/path_sign_verify.go @@ -88,6 +88,10 @@ derivation is enabled; currently only available with ed25519 keys.`, * sha2-256 * sha2-384 * sha2-512 +* sha3-224 +* sha3-256 +* sha3-384 +* sha3-512 Defaults to "sha2-256". Not valid for all key types, including ed25519.`, @@ -183,6 +187,10 @@ derivation is enabled; currently only available with ed25519 keys.`, * sha2-256 * sha2-384 * sha2-512 +* sha3-224 +* sha3-256 +* sha3-384 +* sha3-512 Defaults to "sha2-256". Not valid for all key types.`, }, diff --git a/builtin/logical/transit/path_sign_verify_test.go b/builtin/logical/transit/path_sign_verify_test.go index 40df90838b19..072f8a265fa1 100644 --- a/builtin/logical/transit/path_sign_verify_test.go +++ b/builtin/logical/transit/path_sign_verify_test.go @@ -237,6 +237,26 @@ func testTransit_SignVerify_ECDSA(t *testing.T, bits int) { sig = signRequest(req, false, "") verifyRequest(req, false, "", sig) + req.Data["hash_algorithm"] = "sha2-512" + sig = signRequest(req, false, "") + verifyRequest(req, false, "", sig) + + req.Data["hash_algorithm"] = "sha3-224" + sig = signRequest(req, false, "") + verifyRequest(req, false, "", sig) + + req.Data["hash_algorithm"] = "sha3-256" + sig = signRequest(req, false, "") + verifyRequest(req, false, "", sig) + + req.Data["hash_algorithm"] = "sha3-384" + sig = signRequest(req, false, "") + verifyRequest(req, false, "", sig) + + req.Data["hash_algorithm"] = "sha3-512" + sig = signRequest(req, false, "") + verifyRequest(req, false, "", sig) + req.Data["prehashed"] = true sig = signRequest(req, false, "") verifyRequest(req, false, "", sig) diff --git a/changelog/12792.txt b/changelog/12792.txt index 2785d111bd1b..76f5875e73ff 100644 --- a/changelog/12792.txt +++ b/changelog/12792.txt @@ -1,3 +1,3 @@ -```release-note: feature -core: reading `sys/mounts/:path` now returns the configuration for the secret engine at the given path +```release-note:improvement +core: Reading `sys/mounts/:path` now returns the configuration for the secret engine at the given path ``` diff --git a/changelog/12795.txt b/changelog/12795.txt index fb4ebcc5586f..5b360beda625 100644 --- a/changelog/12795.txt +++ b/changelog/12795.txt @@ -1,3 +1,3 @@ -```release-note:feature +```release-note:improvement core/pki: Support Y10K value in notAfter field to be compliant with IEEE 802.1AR-2018 standard -``` \ No newline at end of file +``` diff --git a/changelog/12976.txt b/changelog/12976.txt new file mode 100644 index 000000000000..1dea358d9db9 --- /dev/null +++ b/changelog/12976.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: Adds flight icons to UI +``` \ No newline at end of file diff --git a/changelog/13022.txt b/changelog/13022.txt index 378b37cbc2b6..bb3d3a69ea07 100644 --- a/changelog/13022.txt +++ b/changelog/13022.txt @@ -1,3 +1,3 @@ ```release-note:improvement -implements Login method in Go client libraries for GCP and Azure auth methods -``` \ No newline at end of file +api: Implements Login method in Go client libraries for GCP and Azure auth methods +``` diff --git a/changelog/13024.txt b/changelog/13024.txt new file mode 100644 index 000000000000..97aea1b90bd3 --- /dev/null +++ b/changelog/13024.txt @@ -0,0 +1,3 @@ +```release-note:feature +**Report in-flight requests**:Adding a trace capability to show in-flight requests, and a new gauge metric to show the total number of in-flight requests +``` diff --git a/changelog/13080.txt b/changelog/13080.txt new file mode 100644 index 000000000000..9c3ed52dc68b --- /dev/null +++ b/changelog/13080.txt @@ -0,0 +1,3 @@ +```release-note:bug +secrets/pki: Default value for key_bits changed to 0, enabling key_type=ec key generation with default value +``` diff --git a/changelog/13178.txt b/changelog/13178.txt index 10bb3b890dae..67f6b2a7b5bc 100644 --- a/changelog/13178.txt +++ b/changelog/13178.txt @@ -1,3 +1,3 @@ ```release-note:improvement -raft: set InitialMmapSize to 100GB on 64bit architectures +storage/raft: Set InitialMmapSize to 100GB on 64bit architectures ``` diff --git a/changelog/13202.txt b/changelog/13202.txt deleted file mode 100644 index 3496665efc9c..000000000000 --- a/changelog/13202.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:feature -/sys/mounts/:path/tune: Add `allowed_managed_keys` field to mount config which is a list of managed key registry entry names that the mount in question is allowed to access. -``` \ No newline at end of file diff --git a/changelog/13324.txt b/changelog/13324.txt new file mode 100644 index 000000000000..f0bced301614 --- /dev/null +++ b/changelog/13324.txt @@ -0,0 +1,3 @@ +```release-note:improvement +core: Replace "master key" terminology with "root key" +``` diff --git a/changelog/13332.txt b/changelog/13332.txt new file mode 100644 index 000000000000..968f32ad8633 --- /dev/null +++ b/changelog/13332.txt @@ -0,0 +1,3 @@ +```release-note:bug +auth/github: Use the Organization ID instead of the Organization name to verify the org membership. +``` diff --git a/changelog/13365.txt b/changelog/13365.txt new file mode 100644 index 000000000000..65284ab7ba53 --- /dev/null +++ b/changelog/13365.txt @@ -0,0 +1,3 @@ +```release-note:improvement +auth/jwt: The Authorization Code flow makes use of the Proof Key for Code Exchange (PKCE) extension. +``` diff --git a/changelog/13367.txt b/changelog/13367.txt new file mode 100644 index 000000000000..9c2a5bb9f2c2 --- /dev/null +++ b/changelog/13367.txt @@ -0,0 +1,3 @@ +```release-note:feature +secrets/transit: Add support for SHA-3 in the Transit backend. +``` diff --git a/changelog/13395.txt b/changelog/13395.txt new file mode 100644 index 000000000000..d8f2e71a566c --- /dev/null +++ b/changelog/13395.txt @@ -0,0 +1,3 @@ +```release-note:improvement +core/identity: Support updating an alias' `custom_metadata` to be empty. +``` diff --git a/changelog/13396.txt b/changelog/13396.txt new file mode 100644 index 000000000000..6600abedf19e --- /dev/null +++ b/changelog/13396.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: Fix client count current month data not showing unless monthly history data exists +``` diff --git a/changelog/13408.txt b/changelog/13408.txt new file mode 100644 index 000000000000..4861b177763b --- /dev/null +++ b/changelog/13408.txt @@ -0,0 +1,3 @@ +```release-note:change +go: Update go version to 1.17.5 +``` diff --git a/command/agent.go b/command/agent.go index 2870227a30d5..5cebb729bd7d 100644 --- a/command/agent.go +++ b/command/agent.go @@ -565,7 +565,7 @@ func (c *AgentCommand) Run(args []string) int { AAD: aad, }) if err != nil { - c.UI.Error(fmt.Sprintf("Error opening persistent cache: %v", err)) + c.UI.Error(fmt.Sprintf("Error opening persistent cache with wrapper: %v", err)) return 1 } diff --git a/command/commands.go b/command/commands.go index bf5daa7474d4..a3846cd414cc 100644 --- a/command/commands.go +++ b/command/commands.go @@ -116,6 +116,8 @@ const ( flagNameAllowedResponseHeaders = "allowed-response-headers" // flagNameTokenType is the flag name used to force a specific token type flagNameTokenType = "token-type" + // flagNameAllowedManagedKeys is the flag name used for auth/secrets enable + flagNameAllowedManagedKeys = "allowed-managed-keys" ) var ( diff --git a/command/debug.go b/command/debug.go index afd4471d0f74..4e6a12a6c3e5 100644 --- a/command/debug.go +++ b/command/debug.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/go-secure-stdlib/gatedwriter" "github.com/hashicorp/go-secure-stdlib/strutil" "github.com/hashicorp/vault/api" + "github.com/hashicorp/vault/sdk/helper/jsonutil" "github.com/hashicorp/vault/sdk/helper/logging" "github.com/hashicorp/vault/sdk/version" "github.com/mholt/archiver" @@ -106,6 +107,7 @@ type DebugCommand struct { metricsCollection []map[string]interface{} replicationStatusCollection []map[string]interface{} serverStatusCollection []map[string]interface{} + inFlightReqStatusCollection []map[string]interface{} // cachedClient holds the client retrieved during preflight cachedClient *api.Client @@ -480,7 +482,7 @@ func (c *DebugCommand) preflight(rawArgs []string) (string, error) { } func (c *DebugCommand) defaultTargets() []string { - return []string{"config", "host", "metrics", "pprof", "replication-status", "server-status", "log"} + return []string{"config", "host", "requests", "metrics", "pprof", "replication-status", "server-status", "log"} } func (c *DebugCommand) captureStaticTargets() error { @@ -492,6 +494,7 @@ func (c *DebugCommand) captureStaticTargets() error { if err != nil { c.captureError("config", err) c.logger.Error("config: error capturing config state", "error", err) + return nil } if resp != nil && resp.Data != nil { @@ -580,6 +583,16 @@ func (c *DebugCommand) capturePollingTargets() error { }) } + // Collect in-flight request status if target is specified + if strutil.StrListContains(c.flagTargets, "requests") { + g.Add(func() error { + c.collectInFlightRequestStatus(ctx) + return nil + }, func(error) { + cancelFunc() + }) + } + if strutil.StrListContains(c.flagTargets, "log") { g.Add(func() error { c.writeLogs(ctx) @@ -611,7 +624,9 @@ func (c *DebugCommand) capturePollingTargets() error { if err := c.persistCollection(c.hostInfoCollection, "host_info.json"); err != nil { c.UI.Error(fmt.Sprintf("Error writing data to %s: %v", "host_info.json", err)) } - + if err := c.persistCollection(c.inFlightReqStatusCollection, "requests.json"); err != nil { + c.UI.Error(fmt.Sprintf("Error writing data to %s: %v", "requests.json", err)) + } return nil } @@ -635,6 +650,7 @@ func (c *DebugCommand) collectHostInfo(ctx context.Context) { resp, err := c.cachedClient.RawRequestWithContext(ctx, r) if err != nil { c.captureError("host", err) + return } if resp != nil { defer resp.Body.Close() @@ -642,6 +658,7 @@ func (c *DebugCommand) collectHostInfo(ctx context.Context) { secret, err := api.ParseSecret(resp.Body) if err != nil { c.captureError("host", err) + return } if secret != nil && secret.Data != nil { hostEntry := secret.Data @@ -829,6 +846,7 @@ func (c *DebugCommand) collectReplicationStatus(ctx context.Context) { resp, err := c.cachedClient.RawRequestWithContext(ctx, r) if err != nil { c.captureError("replication-status", err) + return } if resp != nil { defer resp.Body.Close() @@ -836,6 +854,7 @@ func (c *DebugCommand) collectReplicationStatus(ctx context.Context) { secret, err := api.ParseSecret(resp.Body) if err != nil { c.captureError("replication-status", err) + return } if secret != nil && secret.Data != nil { replicationEntry := secret.Data @@ -880,6 +899,48 @@ func (c *DebugCommand) collectServerStatus(ctx context.Context) { } } +func (c *DebugCommand) collectInFlightRequestStatus(ctx context.Context) { + + idxCount := 0 + intervalTicker := time.Tick(c.flagInterval) + + for { + if idxCount > 0 { + select { + case <-ctx.Done(): + return + case <-intervalTicker: + } + } + + c.logger.Info("capturing in-flight request status", "count", idxCount) + idxCount++ + + req := c.cachedClient.NewRequest("GET", "/v1/sys/in-flight-req") + resp, err := c.cachedClient.RawRequestWithContext(ctx, req) + if err != nil { + c.captureError("requests", err) + return + } + + var data map[string]interface{} + if resp != nil { + defer resp.Body.Close() + err = jsonutil.DecodeJSONFromReader(resp.Body, &data) + if err != nil { + c.captureError("requests", err) + return + } + + statusEntry := map[string]interface{}{ + "timestamp": time.Now().UTC(), + "in_flight_requests": data, + } + c.inFlightReqStatusCollection = append(c.inFlightReqStatusCollection, statusEntry) + } + } +} + // persistCollection writes the collected data for a particular target onto the // specified file. If the collection is empty, it returns immediately. func (c *DebugCommand) persistCollection(collection []map[string]interface{}, outFile string) error { diff --git a/command/debug_test.go b/command/debug_test.go index 885b0de63ef5..7c46eb5bd671 100644 --- a/command/debug_test.go +++ b/command/debug_test.go @@ -235,6 +235,11 @@ func TestDebugCommand_CaptureTargets(t *testing.T) { []string{"server-status"}, []string{"server_status.json"}, }, + { + "in-flight-req", + []string{"requests"}, + []string{"requests.json"}, + }, { "all-minus-pprof", []string{"config", "host", "metrics", "replication-status", "server-status"}, diff --git a/command/operator_init.go b/command/operator_init.go index 2cdba009a729..a8b8e5601024 100644 --- a/command/operator_init.go +++ b/command/operator_init.go @@ -57,10 +57,10 @@ Usage: vault operator init [options] same storage backend in HA mode, you only need to initialize one Vault to initialize the storage backend. - During initialization, Vault generates an in-memory master key and applies - Shamir's secret sharing algorithm to disassemble that master key into a + During initialization, Vault generates an in-memory root key and applies + Shamir's secret sharing algorithm to disassemble that root key into a configuration number of key shares such that a configurable subset of those - key shares must come together to regenerate the master key. These keys are + key shares must come together to regenerate the root key. These keys are often called "unseal keys" in Vault's documentation. This command cannot be run against an already-initialized Vault cluster. @@ -105,7 +105,7 @@ func (c *OperatorInitCommand) Flags() *FlagSets { Target: &c.flagKeyShares, Default: defKeyShares, Completion: complete.PredictAnything, - Usage: "Number of key shares to split the generated master key into. " + + Usage: "Number of key shares to split the generated root key into. " + "This is the number of \"unseal keys\" to generate.", }) @@ -115,7 +115,7 @@ func (c *OperatorInitCommand) Flags() *FlagSets { Target: &c.flagKeyThreshold, Default: defKeyThreshold, Completion: complete.PredictAnything, - Usage: "Number of key shares required to reconstruct the master key. " + + Usage: "Number of key shares required to reconstruct the root key. " + "This must be less than or equal to -key-shares.", }) @@ -447,8 +447,8 @@ func (c *OperatorInitCommand) init(client *api.Client, req *api.InitRequest) int c.UI.Output("") c.UI.Output(wrapAtLength(fmt.Sprintf( - "Vault does not store the generated master key. Without at least %d "+ - "keys to reconstruct the master key, Vault will remain permanently "+ + "Vault does not store the generated root key. Without at least %d "+ + "keys to reconstruct the root key, Vault will remain permanently "+ "sealed!", req.SecretThreshold))) diff --git a/command/operator_rekey.go b/command/operator_rekey.go index ca9a316cb3de..b73349405daa 100644 --- a/command/operator_rekey.go +++ b/command/operator_rekey.go @@ -51,7 +51,7 @@ Usage: vault operator rekey [options] [KEY] Generates a new set of unseal keys. This can optionally change the total number of key shares or the required threshold of those key shares to - reconstruct the master key. This operation is zero downtime, but it requires + reconstruct the root key. This operation is zero downtime, but it requires the Vault is unsealed and a quorum of existing unseal keys are provided. An unseal key may be provided directly on the command line as an argument to @@ -129,7 +129,7 @@ func (c *OperatorRekeyCommand) Flags() *FlagSets { Target: &c.flagKeyShares, Default: 5, Completion: complete.PredictAnything, - Usage: "Number of key shares to split the generated master key into. " + + Usage: "Number of key shares to split the generated root key into. " + "This is the number of \"unseal keys\" to generate.", }) @@ -139,7 +139,7 @@ func (c *OperatorRekeyCommand) Flags() *FlagSets { Target: &c.flagKeyThreshold, Default: 3, Completion: complete.PredictAnything, - Usage: "Number of key shares required to reconstruct the master key. " + + Usage: "Number of key shares required to reconstruct the root key. " + "This must be less than or equal to -key-shares.", }) diff --git a/command/operator_seal.go b/command/operator_seal.go index 9f2ec6656ed7..369ec3215d66 100644 --- a/command/operator_seal.go +++ b/command/operator_seal.go @@ -27,11 +27,11 @@ Usage: vault operator seal [options] Seals the Vault server. Sealing tells the Vault server to stop responding to any operations until it is unsealed. When sealed, the Vault server - discards its in-memory master key to unlock the data, so it is physically + discards its in-memory root key to unlock the data, so it is physically blocked from responding to operations unsealed. If an unseal is in progress, sealing the Vault will reset the unsealing - process. Users will have to re-enter their portions of the master key again. + process. Users will have to re-enter their portions of the root key again. This command does nothing if the Vault server is already sealed. diff --git a/command/operator_unseal.go b/command/operator_unseal.go index da8641ba51de..8cdd06d38408 100644 --- a/command/operator_unseal.go +++ b/command/operator_unseal.go @@ -34,9 +34,9 @@ func (c *OperatorUnsealCommand) Help() string { helpText := ` Usage: vault operator unseal [options] [KEY] - Provide a portion of the master key to unseal a Vault server. Vault starts + Provide a portion of the root key to unseal a Vault server. Vault starts in a sealed state. It cannot perform operations until it is unsealed. This - command accepts a portion of the master key (an "unseal key"). + command accepts a portion of the root key (an "unseal key"). The unseal key can be supplied as an argument to the command, but this is not recommended as the unseal key will be available in your history: diff --git a/command/secrets_enable.go b/command/secrets_enable.go index cb4671ba3943..72b7b89b5585 100644 --- a/command/secrets_enable.go +++ b/command/secrets_enable.go @@ -37,6 +37,7 @@ type SecretsEnableCommand struct { flagSealWrap bool flagExternalEntropyAccess bool flagVersion int + flagAllowedManagedKeys []string } func (c *SecretsEnableCommand) Synopsis() string { @@ -209,6 +210,15 @@ func (c *SecretsEnableCommand) Flags() *FlagSets { Usage: "Select the version of the engine to run. Not supported by all engines.", }) + f.StringSliceVar(&StringSliceVar{ + Name: flagNameAllowedManagedKeys, + Target: &c.flagAllowedManagedKeys, + Usage: "Managed key name(s) that the mount in question is allowed to access. " + + "Note that multiple keys may be specified either by providing the key names " + + "as a comma separated string or by providing this option multiple times, " + + "each time with 1 key.", + }) + return set } @@ -307,6 +317,10 @@ func (c *SecretsEnableCommand) Run(args []string) int { if fl.Name == flagNameAllowedResponseHeaders { mountInput.Config.AllowedResponseHeaders = c.flagAllowedResponseHeaders } + + if fl.Name == flagNameAllowedManagedKeys { + mountInput.Config.AllowedManagedKeys = c.flagAllowedManagedKeys + } }) if err := client.Sys().Mount(mountPath, mountInput); err != nil { diff --git a/command/secrets_enable_test.go b/command/secrets_enable_test.go index bcc581a4e108..f5a54a8cc8da 100644 --- a/command/secrets_enable_test.go +++ b/command/secrets_enable_test.go @@ -113,6 +113,7 @@ func TestSecretsEnableCommand_Run(t *testing.T) { "-passthrough-request-headers", "authorization,authentication", "-passthrough-request-headers", "www-authentication", "-allowed-response-headers", "authorization", + "-allowed-managed-keys", "key1,key2", "-force-no-cache", "pki", }) @@ -162,6 +163,9 @@ func TestSecretsEnableCommand_Run(t *testing.T) { if diff := deep.Equal([]string{"foo,bar"}, mountInfo.Config.AuditNonHMACResponseKeys); len(diff) > 0 { t.Errorf("Failed to find expected values in AuditNonHMACResponseKeys. Difference is: %v", diff) } + if diff := deep.Equal([]string{"key1,key2"}, mountInfo.Config.AllowedManagedKeys); len(diff) > 0 { + t.Errorf("Failed to find expected values in AllowedManagedKeys. Difference is: %v", diff) + } }) diff --git a/command/secrets_tune.go b/command/secrets_tune.go index a7883a618cd0..3e20367ea6e0 100644 --- a/command/secrets_tune.go +++ b/command/secrets_tune.go @@ -30,6 +30,7 @@ type SecretsTuneCommand struct { flagAllowedResponseHeaders []string flagOptions map[string]string flagVersion int + flagAllowedManagedKeys []string } func (c *SecretsTuneCommand) Synopsis() string { @@ -137,6 +138,15 @@ func (c *SecretsTuneCommand) Flags() *FlagSets { Usage: "Select the version of the engine to run. Not supported by all engines.", }) + f.StringSliceVar(&StringSliceVar{ + Name: flagNameAllowedManagedKeys, + Target: &c.flagAllowedManagedKeys, + Usage: "Managed key name(s) that the mount in question is allowed to access. " + + "Note that multiple keys may be specified either by providing the key names " + + "as a comma separated string or by providing this option multiple times, " + + "each time with 1 key.", + }) + return set } @@ -213,6 +223,10 @@ func (c *SecretsTuneCommand) Run(args []string) int { if fl.Name == flagNameAllowedResponseHeaders { mountConfigInput.AllowedResponseHeaders = c.flagAllowedResponseHeaders } + + if fl.Name == flagNameAllowedManagedKeys { + mountConfigInput.AllowedManagedKeys = c.flagAllowedManagedKeys + } }) if err := client.Sys().TuneMount(mountPath, mountConfigInput); err != nil { diff --git a/command/secrets_tune_test.go b/command/secrets_tune_test.go index de732873790e..f51b8fb34b78 100644 --- a/command/secrets_tune_test.go +++ b/command/secrets_tune_test.go @@ -170,6 +170,7 @@ func TestSecretsTuneCommand_Run(t *testing.T) { "-passthrough-request-headers", "authorization", "-passthrough-request-headers", "www-authentication", "-allowed-response-headers", "authorization,www-authentication", + "-allowed-managed-keys", "key1,key2", "-listing-visibility", "unauth", "mount_tune_integration/", }) @@ -216,6 +217,9 @@ func TestSecretsTuneCommand_Run(t *testing.T) { if diff := deep.Equal([]string{"foo,bar"}, mountInfo.Config.AuditNonHMACResponseKeys); len(diff) > 0 { t.Errorf("Failed to find expected values in AuditNonHMACResponseKeys. Difference is: %v", diff) } + if diff := deep.Equal([]string{"key1,key2"}, mountInfo.Config.AllowedManagedKeys); len(diff) > 0 { + t.Errorf("Failed to find expected values in AllowedManagedKeys. Difference is: %v", diff) + } }) t.Run("flags_description", func(t *testing.T) { diff --git a/command/server.go b/command/server.go index 718009b8cf41..5b8eb634af8e 100644 --- a/command/server.go +++ b/command/server.go @@ -1547,6 +1547,9 @@ func (c *ServerCommand) Run(args []string) int { c.logger.Error(err.Error()) } + // Setting log request with the new value in the config after reload + core.ReloadLogRequestsLevel() + if config.LogLevel != "" { configLogLevel := strings.ToLower(strings.TrimSpace(config.LogLevel)) switch configLogLevel { diff --git a/command/server/config.go b/command/server/config.go index c478906a7928..e79cce6e9cdb 100644 --- a/command/server/config.go +++ b/command/server/config.go @@ -8,10 +8,13 @@ import ( "io/ioutil" "os" "path/filepath" + "regexp" "strconv" "strings" "time" + wrapping "github.com/hashicorp/go-kms-wrapping" + "github.com/hashicorp/go-multierror" "github.com/hashicorp/go-secure-stdlib/parseutil" "github.com/hashicorp/hcl" @@ -23,6 +26,8 @@ var entConfigValidate = func(_ *Config, _ string) []configutil.ConfigError { return nil } +var kmsLibraryValidator = defaultKmsLibraryValidator + // Config is the configuration for the vault server. type Config struct { UnusedKeys configutil.UnusedKeyMap `hcl:",unusedKeyPositions"` @@ -77,11 +82,16 @@ type Config struct { EnableResponseHeaderHostname bool `hcl:"-"` EnableResponseHeaderHostnameRaw interface{} `hcl:"enable_response_header_hostname"` + LogRequestsLevel string `hcl:"-"` + LogRequestsLevelRaw interface{} `hcl:"log_requests_level"` + EnableResponseHeaderRaftNodeID bool `hcl:"-"` EnableResponseHeaderRaftNodeIDRaw interface{} `hcl:"enable_response_header_raft_node_id"` License string `hcl:"-"` LicensePath string `hcl:"license_path"` + + KmsLibraries map[string]*KMSLibrary `hcl:"-"` } const ( @@ -99,6 +109,9 @@ func (c *Config) Validate(sourceFilePath string) []configutil.ConfigError { for _, l := range c.Listeners { results = append(results, l.Validate(sourceFilePath)...) } + for _, kmslibrary := range c.KmsLibraries { + results = append(results, kmslibrary.Validate(sourceFilePath)...) + } results = append(results, c.validateEnt(sourceFilePath)...) return results } @@ -168,6 +181,24 @@ func (b *ServiceRegistration) GoString() string { return fmt.Sprintf("*%#v", *b) } +// KMSLibrary is a per-server configuration that will be further augmented with managed key configuration to +// build up a KMS wrapper type to delegate encryption operations to HSMs +type KMSLibrary struct { + UnusedKeys configutil.UnusedKeyMap `hcl:",unusedKeyPositions"` + FoundKeys []string `hcl:",decodedFields"` + Type string `hcl:"-"` + Name string `hcl:"name"` + Library string `hcl:"library"` +} + +func (k *KMSLibrary) Validate(source string) []configutil.ConfigError { + return configutil.ValidateUnusedFields(k.UnusedKeys, source) +} + +func (k *KMSLibrary) GoString() string { + return fmt.Sprintf("*%#v", *k) +} + func NewConfig() *Config { return &Config{ SharedConfig: new(configutil.SharedConfig), @@ -292,6 +323,11 @@ func (c *Config) Merge(c2 *Config) *Config { result.EnableResponseHeaderHostname = c2.EnableResponseHeaderHostname } + result.LogRequestsLevel = c.LogRequestsLevel + if c2.LogRequestsLevel != "" { + result.LogRequestsLevel = c2.LogRequestsLevel + } + result.EnableResponseHeaderRaftNodeID = c.EnableResponseHeaderRaftNodeID if c2.EnableResponseHeaderRaftNodeID { result.EnableResponseHeaderRaftNodeID = c2.EnableResponseHeaderRaftNodeID @@ -480,6 +516,11 @@ func ParseConfig(d, source string) (*Config, error) { } } + if result.LogRequestsLevelRaw != nil { + result.LogRequestsLevel = strings.ToLower(strings.TrimSpace(result.LogRequestsLevelRaw.(string))) + result.LogRequestsLevelRaw = "" + } + if result.EnableResponseHeaderRaftNodeIDRaw != nil { if result.EnableResponseHeaderRaftNodeID, err = parseutil.ParseBool(result.EnableResponseHeaderRaftNodeIDRaw); err != nil { return nil, err @@ -528,6 +569,14 @@ func ParseConfig(d, source string) (*Config, error) { } } + // Parse KMSLibraries sections if any + if o := list.Filter("kms_library"); len(o.Items) > 0 { + delete(result.UnusedKeys, "kms_library") + if err := parseKmsLibraries(result, o); err != nil { + return nil, fmt.Errorf("error parsing 'kms_library': %w", err) + } + } + entConfig := &(result.entConfig) if err := entConfig.parseConfig(list); err != nil { return nil, fmt.Errorf("error parsing enterprise config: %w", err) @@ -794,6 +843,75 @@ func parseServiceRegistration(result *Config, list *ast.ObjectList, name string) return nil } +func parseKmsLibraries(result *Config, list *ast.ObjectList) error { + result.KmsLibraries = make(map[string]*KMSLibrary, len(list.Items)) + + for _, item := range list.Items { + library, err := decodeKmsLibrary(item) + if err != nil { + return err + } + + if err := validateKmsLibrary(library); err != nil { + return err + } + + if _, ok := result.KmsLibraries[library.Name]; ok { + return fmt.Errorf("duplicated kms_library configuration sections with name %s", library.Name) + } + + result.KmsLibraries[library.Name] = library + } + return nil +} + +func decodeKmsLibrary(item *ast.ObjectItem) (*KMSLibrary, error) { + library := &KMSLibrary{} + if err := hcl.DecodeObject(&library, item.Val); err != nil { + return nil, multierror.Prefix(err, "kms_library") + } + + if len(item.Keys) != 1 { + return nil, errors.New("kms_library section was missing a type") + } + + library.Type = strings.ToLower(item.Keys[0].Token.Value().(string)) + library.Name = strings.ToLower(library.Name) + + return library, nil +} + +func defaultKmsLibraryValidator(kms *KMSLibrary) error { + switch kms.Type { + case wrapping.PKCS11: + return fmt.Errorf("KMS type 'pkcs11' requires the Vault Enterprise HSM binary") + + default: + return fmt.Errorf("unknown KMS type %q", kms.Type) + } +} + +func validateKmsLibrary(kmsConfig *KMSLibrary) error { + if kmsConfig.Library == "" { + return fmt.Errorf("library key can not be blank within kms_library type: %s", kmsConfig.Type) + } + + if kmsConfig.Name == "" { + return fmt.Errorf("name key can not be blank within kms_library type: %s", kmsConfig.Type) + } + + nameRegex := regexp.MustCompile("^[\\w._-]+$") + if !nameRegex.MatchString(kmsConfig.Name) { + return fmt.Errorf("value ('%s') for name field contained invalid characters within kms_library type: %s", kmsConfig.Name, kmsConfig.Type) + } + + if err := kmsLibraryValidator(kmsConfig); err != nil { + return err + } + + return nil +} + // Sanitized returns a copy of the config with all values that are considered // sensitive stripped. It also strips all `*Raw` values that are mainly // used for parsing. @@ -840,6 +958,8 @@ func (c *Config) Sanitized() map[string]interface{} { "enable_response_header_hostname": c.EnableResponseHeaderHostname, "enable_response_header_raft_node_id": c.EnableResponseHeaderRaftNodeID, + + "log_requests_level": c.LogRequestsLevel, } for k, v := range sharedResult { result[k] = v diff --git a/command/server/config_oss_test.go b/command/server/config_oss_test.go index 6f466ddf6d01..84abc4c8b91f 100644 --- a/command/server/config_oss_test.go +++ b/command/server/config_oss_test.go @@ -17,3 +17,7 @@ func TestLoadConfigFile_json2(t *testing.T) { func TestParseEntropy(t *testing.T) { testParseEntropy(t, true) } + +func TestKmsLibraryFailsForNonHSMBinary(t *testing.T) { + testKmsLibraryFailsForNonHSMBinary(t) +} diff --git a/command/server/config_test_helpers.go b/command/server/config_test_helpers.go index 106ad7dce22b..310e6b3b4720 100644 --- a/command/server/config_test_helpers.go +++ b/command/server/config_test_helpers.go @@ -701,6 +701,7 @@ func testConfig_Sanitized(t *testing.T) { "enable_ui": true, "enable_response_header_hostname": false, "enable_response_header_raft_node_id": false, + "log_requests_level": "basic", "ha_storage": map[string]interface{}{ "cluster_addr": "top_level_cluster_addr", "disable_clustering": true, @@ -1063,3 +1064,26 @@ func testConfigRaftAutopilot(t *testing.T) { t.Fatal(diff) } } + +func testKmsLibraryFailsForNonHSMBinary(t *testing.T) { + config := ` +ui = false +storage "file" { + path = "/tmp/test" +} + +listener "tcp" { + address = "0.0.0.0:8200" + tls_cert_file = "/opt/vault/tls/tls.crt" + tls_key_file = "/opt/vault/tls/tls.key" +} + +kms_library "pkcs11" { + name="Logical1" + library="a library" +} +` + _, err := ParseConfig(config, "") + require.Error(t, err) + require.Contains(t, err.Error(), "requires the Vault Enterprise HSM binary") +} diff --git a/command/server/listener_test.go b/command/server/listener_test.go index 5b2271eefc34..cda5b733e26b 100644 --- a/command/server/listener_test.go +++ b/command/server/listener_test.go @@ -6,6 +6,7 @@ import ( "io" "net" "testing" + ) type testListenerConnFn func(net.Listener) (net.Conn, error) @@ -66,3 +67,15 @@ func testListenerImpl(t *testing.T, ln net.Listener, connFn testListenerConnFn, t.Fatalf("bad: %v", buf.String()) } } + + +func TestProfilingUnauthenticatedInFlightAccess(t *testing.T) { + + config, err := LoadConfigFile("./test-fixtures/unauth_in_flight_access.hcl") + if err != nil { + t.Fatalf("Error encountered when loading config %+v", err) + } + if !config.Listeners[0].InFlightRequestLogging.UnauthenticatedInFlightAccess { + t.Fatalf("failed to read UnauthenticatedInFlightAccess") + } +} \ No newline at end of file diff --git a/command/server/test-fixtures/config3.hcl b/command/server/test-fixtures/config3.hcl index 3394d04f57f5..1a4894d194bc 100644 --- a/command/server/test-fixtures/config3.hcl +++ b/command/server/test-fixtures/config3.hcl @@ -1,5 +1,6 @@ disable_cache = true disable_mlock = true +log_requests_level = "Basic" ui = true diff --git a/command/server/test-fixtures/unauth_in_flight_access.hcl b/command/server/test-fixtures/unauth_in_flight_access.hcl new file mode 100644 index 000000000000..eda6641276f1 --- /dev/null +++ b/command/server/test-fixtures/unauth_in_flight_access.hcl @@ -0,0 +1,9 @@ +storage "inmem" {} +listener "tcp" { + address = "127.0.0.1:8200" + tls_disable = true + inflight_requests_logging { + unauthenticated_in_flight_requests_access = true + } +} +disable_mlock = true diff --git a/go.mod b/go.mod index 4c3bcc5bf4f5..1d9522817f14 100644 --- a/go.mod +++ b/go.mod @@ -94,7 +94,7 @@ require ( github.com/hashicorp/vault-plugin-auth-centrify v0.10.0 github.com/hashicorp/vault-plugin-auth-cf v0.10.0 github.com/hashicorp/vault-plugin-auth-gcp v0.11.2 - github.com/hashicorp/vault-plugin-auth-jwt v0.11.2 + github.com/hashicorp/vault-plugin-auth-jwt v0.11.3 github.com/hashicorp/vault-plugin-auth-kerberos v0.5.0 github.com/hashicorp/vault-plugin-auth-kubernetes v0.11.3 github.com/hashicorp/vault-plugin-auth-oci v0.9.0 diff --git a/go.sum b/go.sum index fa0563c53cb7..9e6374cd0dce 100644 --- a/go.sum +++ b/go.sum @@ -938,8 +938,8 @@ github.com/hashicorp/vault-plugin-auth-cf v0.10.0 h1:c9jepaNQXfPNl7ryufVP9RBKb5S github.com/hashicorp/vault-plugin-auth-cf v0.10.0/go.mod h1:4HM4amMEcCyoLZNNjyz5AYILIlhMLTErxrinM3Vopy4= github.com/hashicorp/vault-plugin-auth-gcp v0.11.2 h1:nchN/UFZDZFQbNJHDwq86vDRk6JUkTS999aZcfpxbVY= github.com/hashicorp/vault-plugin-auth-gcp v0.11.2/go.mod h1:HJc8ih7gLNpBJYTwFlXJA3H48LKsP8mlVKYtPWfRkHs= -github.com/hashicorp/vault-plugin-auth-jwt v0.11.2 h1:+dRT24GSDXKMF+phD+ejFDax8cI0gri6saj9p7cCXw0= -github.com/hashicorp/vault-plugin-auth-jwt v0.11.2/go.mod h1:jzjDdssus8sw8G6NOP7kNFMEeIvrjXvPHUR3pEn5+r0= +github.com/hashicorp/vault-plugin-auth-jwt v0.11.3 h1:uo7Gz81YqiYjg1ne4ZvT5csV/L4UT/9jlNVdCdb1jEs= +github.com/hashicorp/vault-plugin-auth-jwt v0.11.3/go.mod h1:jzjDdssus8sw8G6NOP7kNFMEeIvrjXvPHUR3pEn5+r0= github.com/hashicorp/vault-plugin-auth-kerberos v0.5.0 h1:oORxeqOraVVLQrb+z3fj5JayPmH/JBxJWGywZ8ZRJt0= github.com/hashicorp/vault-plugin-auth-kerberos v0.5.0/go.mod h1:eqjae8tMBpAWgJNk1NjV/vtJYXQRZnYudUkBFowz3bY= github.com/hashicorp/vault-plugin-auth-kubernetes v0.11.3 h1:VTl62rRNhcALzsLw8romBZfTRpVna2IeLTN0kAQyXvY= diff --git a/helper/constants/fips.go b/helper/constants/fips.go new file mode 100644 index 000000000000..4b8a99991c7a --- /dev/null +++ b/helper/constants/fips.go @@ -0,0 +1,8 @@ +// +build !fips_140_3 + +package constants + +// IsFIPS returns true if Vault is operating in a FIPS-140-{2,3} mode. +func IsFIPS() bool { + return false +} diff --git a/http/handler.go b/http/handler.go index 02a153eb4a0f..636566a535c5 100644 --- a/http/handler.go +++ b/http/handler.go @@ -25,6 +25,7 @@ import ( "github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/go-secure-stdlib/parseutil" "github.com/hashicorp/go-sockaddr" + "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/internalshared/configutil" "github.com/hashicorp/vault/sdk/helper/consts" @@ -195,6 +196,11 @@ func Handler(props *vault.HandlerProperties) http.Handler { mux.Handle("/v1/sys/pprof/", handleLogicalNoForward(core)) } + if props.ListenerConfig != nil && props.ListenerConfig.InFlightRequestLogging.UnauthenticatedInFlightAccess { + mux.Handle("/v1/sys/in-flight-req", handleUnAuthenticatedInFlightRequest(core)) + } else { + mux.Handle("/v1/sys/in-flight-req", handleLogicalNoForward(core)) + } additionalRoutes(mux, core) } @@ -314,8 +320,11 @@ func wrapGenericHandler(core *vault.Core, h http.Handler, props *vault.HandlerPr customHeaders = listenerCustomHeaders.StatusCodeHeaderMap } } + // saving start time for the in-flight requests + inFlightReqStartTime := time.Now() nw := logical.NewStatusHeaderResponseWriter(w, customHeaders) + // Set the Cache-Control header for all the responses returned // by Vault nw.Header().Set("Cache-Control", "no-store") @@ -367,6 +376,43 @@ func wrapGenericHandler(core *vault.Core, h http.Handler, props *vault.HandlerPr return } + // The uuid for the request is going to be generated when a logical + // request is generated. But, here we generate one to be able to track + // in-flight requests, and use that to update the req data with clientID + inFlightReqID, err := uuid.GenerateUUID() + if err != nil { + respondError(nw, http.StatusInternalServerError, fmt.Errorf("failed to generate an identifier for the in-flight request")) + } + // adding an entry to the context to enable updating in-flight + // data with ClientID in the logical layer + r = r.WithContext(context.WithValue(r.Context(), logical.CtxKeyInFlightRequestID{}, inFlightReqID)) + + // extracting the client address to be included in the in-flight request + var clientAddr string + headers := r.Header[textproto.CanonicalMIMEHeaderKey("X-Forwarded-For")] + if len(headers) == 0 { + clientAddr = r.RemoteAddr + } else { + clientAddr = headers[0] + } + + // getting the request method + requestMethod := r.Method + + // Storing the in-flight requests. Path should include namespace as well + core.StoreInFlightReqData( + inFlightReqID, + vault.InFlightReqData { + StartTime: inFlightReqStartTime, + ReqPath: r.URL.Path, + ClientRemoteAddr: clientAddr, + Method: requestMethod, + }) + defer func() { + // Not expecting this fail, so skipping the assertion check + core.FinalizeInFlightReqData(inFlightReqID, nw.StatusCode) + }() + // Setting the namespace in the header to be included in the error message ns := r.Header.Get(consts.NamespaceHeaderName) if ns != "" { diff --git a/http/handler_test.go b/http/handler_test.go index b82c1ede3898..9cefe5aaa59f 100644 --- a/http/handler_test.go +++ b/http/handler_test.go @@ -294,6 +294,45 @@ func TestHandler_CacheControlNoStore(t *testing.T) { } } +func TestHandler_InFlightRequest(t *testing.T) { + core, _, token := vault.TestCoreUnsealed(t) + ln, addr := TestServer(t, core) + defer ln.Close() + TestServerAuth(t, addr, token) + + req, err := http.NewRequest("GET", addr+"/v1/sys/in-flight-req", nil) + if err != nil { + t.Fatalf("err: %s", err) + } + req.Header.Set(consts.AuthHeaderName, token) + + client := cleanhttp.DefaultClient() + resp, err := client.Do(req) + if err != nil { + t.Fatalf("err: %s", err) + } + + if resp == nil { + t.Fatalf("nil response") + } + + var actual map[string]interface{} + testResponseStatus(t, resp, 200) + testResponseBody(t, resp, &actual) + if actual == nil || len(actual) == 0 { + t.Fatal("expected to get at least one in-flight request, got nil or zero length map") + } + for _, v := range actual { + reqInfo, ok := v.(map[string]interface{}) + if !ok { + t.Fatal("failed to read in-flight request") + } + if reqInfo["request_path"] != "/v1/sys/in-flight-req" { + t.Fatalf("expected /v1/sys/in-flight-req in-flight request path, got %s", actual["request_path"]) + } + } +} + // TestHandler_MissingToken tests the response / error code if a request comes // in with a missing client token. See // https://github.com/hashicorp/vault/issues/8377 diff --git a/http/logical.go b/http/logical.go index a7730033b351..4a978b2d4997 100644 --- a/http/logical.go +++ b/http/logical.go @@ -183,7 +183,7 @@ func buildLogicalRequestNoAuth(perfStandby bool, w http.ResponseWriter, r *http. requestId, err := uuid.GenerateUUID() if err != nil { - return nil, nil, http.StatusBadRequest, fmt.Errorf("failed to generate identifier for the request: %w", err) + return nil, nil, http.StatusInternalServerError, fmt.Errorf("failed to generate identifier for the request: %w", err) } req := &logical.Request{ diff --git a/http/sys_config_state_test.go b/http/sys_config_state_test.go index 543809ce2bd5..2abdf38e65fb 100644 --- a/http/sys_config_state_test.go +++ b/http/sys_config_state_test.go @@ -48,6 +48,7 @@ func TestSysConfigState_Sanitized(t *testing.T) { "plugin_directory": "", "enable_response_header_hostname": false, "enable_response_header_raft_node_id": false, + "log_requests_level": "", } expected = map[string]interface{}{ diff --git a/http/sys_in_flight_requests.go b/http/sys_in_flight_requests.go new file mode 100644 index 000000000000..cd010b2af846 --- /dev/null +++ b/http/sys_in_flight_requests.go @@ -0,0 +1,23 @@ +package http + +import ( + "net/http" + + "github.com/hashicorp/vault/vault" +) + +func handleUnAuthenticatedInFlightRequest(core *vault.Core) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case "GET": + default: + respondError(w, http.StatusMethodNotAllowed, nil) + return + } + + currentInFlightReqMap := core.LoadInFlightReqData() + + respondOk(w, currentInFlightReqMap) + + }) +} diff --git a/http/sys_in_flight_requests_test.go b/http/sys_in_flight_requests_test.go new file mode 100644 index 000000000000..de64d708c68f --- /dev/null +++ b/http/sys_in_flight_requests_test.go @@ -0,0 +1,46 @@ +package http + +import ( + "testing" + + "github.com/hashicorp/vault/internalshared/configutil" + "github.com/hashicorp/vault/vault" +) + +func TestInFlightRequestUnauthenticated(t *testing.T) { + conf := &vault.CoreConfig{} + core, _, token := vault.TestCoreUnsealedWithConfig(t, conf) + ln, addr := TestServer(t, core) + TestServerAuth(t, addr, token) + + // Default: Only authenticated access + resp := testHttpGet(t, "", addr+"/v1/sys/in-flight-req") + testResponseStatus(t, resp, 403) + resp = testHttpGet(t, token, addr+"/v1/sys/in-flight-req") + testResponseStatus(t, resp, 200) + + // Close listener + ln.Close() + + // Setup new custom listener with unauthenticated metrics access + ln, addr = TestListener(t) + props := &vault.HandlerProperties{ + Core: core, + ListenerConfig: &configutil.Listener{ + InFlightRequestLogging: configutil.ListenerInFlightRequestLogging{ + UnauthenticatedInFlightAccess: true, + }, + }, + } + TestServerWithListenerAndProperties(t, ln, addr, core, props) + defer ln.Close() + TestServerAuth(t, addr, token) + + // Test without token + resp = testHttpGet(t, "", addr+"/v1/sys/in-flight-req") + testResponseStatus(t, resp, 200) + + // Should also work with token + resp = testHttpGet(t, token, addr+"/v1/sys/in-flight-req") + testResponseStatus(t, resp, 200) +} diff --git a/internalshared/configutil/listener.go b/internalshared/configutil/listener.go index 7260c9cc1f99..c4617731e282 100644 --- a/internalshared/configutil/listener.go +++ b/internalshared/configutil/listener.go @@ -24,9 +24,15 @@ type ListenerTelemetry struct { } type ListenerProfiling struct { - UnusedKeys UnusedKeyMap `hcl:",unusedKeyPositions"` - UnauthenticatedPProfAccess bool `hcl:"-"` - UnauthenticatedPProfAccessRaw interface{} `hcl:"unauthenticated_pprof_access,alias:UnauthenticatedPProfAccessRaw"` + UnusedKeys UnusedKeyMap `hcl:",unusedKeyPositions"` + UnauthenticatedPProfAccess bool `hcl:"-"` + UnauthenticatedPProfAccessRaw interface{} `hcl:"unauthenticated_pprof_access,alias:UnauthenticatedPProfAccessRaw"` +} + +type ListenerInFlightRequestLogging struct { + UnusedKeys UnusedKeyMap `hcl:",unusedKeyPositions"` + UnauthenticatedInFlightAccess bool `hcl:"-"` + UnauthenticatedInFlightAccessRaw interface{} `hcl:"unauthenticated_in_flight_requests_access,alias:unauthenticatedInFlightAccessRaw"` } // Listener is the listener configuration for the server. @@ -87,8 +93,9 @@ type Listener struct { SocketUser string `hcl:"socket_user"` SocketGroup string `hcl:"socket_group"` - Telemetry ListenerTelemetry `hcl:"telemetry"` - Profiling ListenerProfiling `hcl:"profiling"` + Telemetry ListenerTelemetry `hcl:"telemetry"` + Profiling ListenerProfiling `hcl:"profiling"` + InFlightRequestLogging ListenerInFlightRequestLogging `hcl:"inflight_requests_logging"` // RandomPort is used only for some testing purposes RandomPort bool `hcl:"-"` @@ -345,6 +352,17 @@ func ParseListeners(result *SharedConfig, list *ast.ObjectList) error { } } + // InFlight Request logging + { + if l.InFlightRequestLogging.UnauthenticatedInFlightAccessRaw != nil { + if l.InFlightRequestLogging.UnauthenticatedInFlightAccess, err = parseutil.ParseBool(l.InFlightRequestLogging.UnauthenticatedInFlightAccessRaw); err != nil { + return multierror.Prefix(fmt.Errorf("invalid value for inflight_requests_logging.unauthenticated_in_flight_requests_access: %w", err), fmt.Sprintf("listeners.%d", i)) + } + + l.InFlightRequestLogging.UnauthenticatedInFlightAccessRaw = "" + } + } + // CORS { if l.CorsEnabledRaw != nil { diff --git a/physical/raft/raft.go b/physical/raft/raft.go index f88d66c5bea0..52826c09c4aa 100644 --- a/physical/raft/raft.go +++ b/physical/raft/raft.go @@ -1144,7 +1144,7 @@ func (b *RaftBackend) SnapshotHTTP(out *logical.HTTPResponseWriter, access *seal // Snapshot takes a raft snapshot, packages it into a archive file and writes it // to the provided writer. Seal access is used to encrypt the SHASUM file so we -// can validate the snapshot was taken using the same master keys or not. +// can validate the snapshot was taken using the same root keys or not. func (b *RaftBackend) Snapshot(out io.Writer, access *seal.Access) error { b.l.RLock() defer b.l.RUnlock() @@ -1167,7 +1167,7 @@ func (b *RaftBackend) Snapshot(out io.Writer, access *seal.Access) error { // WriteSnapshotToTemp reads a snapshot archive off the provided reader, // extracts the data and writes the snapshot to a temporary file. The seal // access is used to decrypt the SHASUM file in the archive to ensure this -// snapshot has the same master key as the running instance. If the provided +// snapshot has the same root key as the running instance. If the provided // access is nil then it will skip that validation. func (b *RaftBackend) WriteSnapshotToTemp(in io.ReadCloser, access *seal.Access) (*os.File, func(), raft.SnapshotMeta, error) { b.l.RLock() diff --git a/scripts/docker/Dockerfile b/scripts/docker/Dockerfile index eca04152d538..71aa724b42cf 100644 --- a/scripts/docker/Dockerfile +++ b/scripts/docker/Dockerfile @@ -1,7 +1,7 @@ # Multi-stage builder to avoid polluting users environment with wrong # architecture binaries. Since this binary is used in an alpine container, # we're explicitly compiling for 'linux/amd64' -ARG VERSION=1.17.2 +ARG VERSION=1.17.5 FROM golang:${VERSION} AS builder diff --git a/scripts/docker/Dockerfile.ui b/scripts/docker/Dockerfile.ui index c8500183c69c..f49924c40c93 100644 --- a/scripts/docker/Dockerfile.ui +++ b/scripts/docker/Dockerfile.ui @@ -3,7 +3,7 @@ # we're explicitly compiling for 'linux/amd64' FROM debian:buster AS builder -ARG VERSION=1.17.2 +ARG VERSION=1.17.5 ARG CGO_ENABLED=0 ARG BUILD_TAGS ENV JOBS=2 diff --git a/sdk/helper/certutil/helpers.go b/sdk/helper/certutil/helpers.go index 8d47d619dc28..f7bd782a2bd1 100644 --- a/sdk/helper/certutil/helpers.go +++ b/sdk/helper/certutil/helpers.go @@ -33,6 +33,14 @@ import ( cbasn1 "golang.org/x/crypto/cryptobyte/asn1" ) +const rsaMinimumSecureKeySize = 2048 + +// Mapping of key types to default key lengths +var defaultAlgorithmKeyBits = map[string]int { + "rsa": 2048, + "ec": 256, +} + // Mapping of NIST P-Curve's key length to expected signature bits. var expectedNISTPCurveHashBits = map[int]int{ 224: 256, @@ -533,14 +541,27 @@ func StringToOid(in string) (asn1.ObjectIdentifier, error) { return asn1.ObjectIdentifier(ret), nil } -// Validates that the combination of keyType, keyBits, and hashBits are -// valid together; replaces individual calls to ValidateSignatureLength and -// ValidateKeyTypeLength. -func ValidateKeyTypeSignatureLength(keyType string, keyBits int, hashBits *int) error { - if err := ValidateKeyTypeLength(keyType, keyBits); err != nil { - return err - } +// Returns default key bits for the specified key type, or the present value +// if keyBits is non-zero. +func DefaultOrValueKeyBits(keyType string, keyBits int) (int, error) { + if keyBits == 0 { + newValue, present := defaultAlgorithmKeyBits[keyType] + if present { + keyBits = newValue + } /* else { + // We cannot return an error here as ed25519 (and potentially ed448 + // in the future) aren't in defaultAlgorithmKeyBits -- the value of + // the keyBits parameter is ignored under that algorithm. + } */ + } + + return keyBits, nil +} +// Returns default signature hash bit length for the specified key type and +// bits, or the present value if hashBits is non-zero. Returns an error under +// certain internal circumstances. +func DefaultOrValueHashBits(keyType string, keyBits int, hashBits int) (int, error) { if keyType == "ec" { // To comply with BSI recommendations Section 4.2 and Mozilla root // store policy section 5.1.2, enforce that NIST P-curves use a hash @@ -548,35 +569,72 @@ func ValidateKeyTypeSignatureLength(keyType string, keyBits int, hashBits *int) // the "ec" key type. expectedHashBits := expectedNISTPCurveHashBits[keyBits] - if expectedHashBits != *hashBits && *hashBits != 0 { - return fmt.Errorf("unsupported signature hash algorithm length (%d) for NIST P-%d", *hashBits, keyBits) - } else if *hashBits == 0 { - *hashBits = expectedHashBits + if expectedHashBits != hashBits && hashBits != 0 { + return hashBits, fmt.Errorf("unsupported signature hash algorithm length (%d) for NIST P-%d", hashBits, keyBits) + } else if hashBits == 0 { + hashBits = expectedHashBits } - } else if keyType == "rsa" && *hashBits == 0 { - // To match previous behavior (and ignoring recommendations of hash - // size to match RSA key sizes), default to SHA-2-256. - *hashBits = 256 - } else if keyType == "ed25519" { + } else if keyType == "rsa" && hashBits == 0 { + // To match previous behavior (and ignoring NIST's recommendations for + // hash size to align with RSA key sizes), default to SHA-2-256. + hashBits = 256 + } else if keyType == "ed25519" || keyType == "ed448" { // No-op; ed25519 and ed448 internally specify their own hash and // we do not need to select one. Double hashing isn't supported in - // certificate signing. - return nil + // certificate signing and we must + return 0, nil + } + + return hashBits, nil +} + +// Validates that the combination of keyType, keyBits, and hashBits are +// valid together; replaces individual calls to ValidateSignatureLength and +// ValidateKeyTypeLength. Also updates the value of keyBits and hashBits on +// return. +func ValidateDefaultOrValueKeyTypeSignatureLength(keyType string, keyBits int, hashBits int) (int, int, error) { + var err error + + if keyBits, err = DefaultOrValueKeyBits(keyType, keyBits); err != nil { + return keyBits, hashBits, err + } + + if err = ValidateKeyTypeLength(keyType, keyBits); err != nil { + return keyBits, hashBits, err + } + + if hashBits, err = DefaultOrValueHashBits(keyType, keyBits, hashBits); err != nil { + return keyBits, hashBits, err } // Note that this check must come after we've selected a value for // hashBits above, in the event it was left as the default, but we // were allowed to update it. - if err := ValidateSignatureLength(*hashBits); err != nil || *hashBits == 0 { - return err + if err = ValidateSignatureLength(keyType, hashBits); err != nil { + return keyBits, hashBits, err } - return nil + return keyBits, hashBits, nil } // Validates that the length of the hash (in bits) used in the signature // calculation is a known, approved value. -func ValidateSignatureLength(hashBits int) error { +func ValidateSignatureLength(keyType string, hashBits int) error { + if keyType == "ed25519" || keyType == "ed448" { + // ed25519 and ed448 include built-in hashing and is not externally + // configurable. There are three modes for each of these schemes: + // + // 1. Built-in hash (default, used in TLS, x509). + // 2. Double hash (notably used in some block-chain implementations, + // but largely regarded as a specialized use case with security + // concerns). + // 3. No hash (bring your own hash function, less commonly used). + // + // In all cases, we won't have a hash algorithm to validate here, so + // return nil. + return nil + } + switch hashBits { case 256: case 384: @@ -584,12 +642,17 @@ func ValidateSignatureLength(hashBits int) error { default: return fmt.Errorf("unsupported hash signature algorithm: %d", hashBits) } + return nil } func ValidateKeyTypeLength(keyType string, keyBits int) error { switch keyType { case "rsa": + if keyBits < rsaMinimumSecureKeySize { + return fmt.Errorf("RSA keys < %d bits are unsafe and not supported: got %d", rsaMinimumSecureKeySize, keyBits) + } + switch keyBits { case 2048: case 3072: diff --git a/sdk/helper/keysutil/consts.go b/sdk/helper/keysutil/consts.go index 59142a399a38..2a83ab849661 100644 --- a/sdk/helper/keysutil/consts.go +++ b/sdk/helper/keysutil/consts.go @@ -5,6 +5,8 @@ import ( "crypto/sha256" "crypto/sha512" "hash" + + "golang.org/x/crypto/sha3" ) type HashType uint32 @@ -16,6 +18,10 @@ const ( HashTypeSHA2256 HashTypeSHA2384 HashTypeSHA2512 + HashTypeSHA3224 + HashTypeSHA3256 + HashTypeSHA3384 + HashTypeSHA3512 ) type MarshalingType uint32 @@ -33,6 +39,10 @@ var ( "sha2-256": HashTypeSHA2256, "sha2-384": HashTypeSHA2384, "sha2-512": HashTypeSHA2512, + "sha3-224": HashTypeSHA3224, + "sha3-256": HashTypeSHA3256, + "sha3-384": HashTypeSHA3384, + "sha3-512": HashTypeSHA3512, } HashFuncMap = map[HashType]func() hash.Hash{ @@ -41,6 +51,10 @@ var ( HashTypeSHA2256: sha256.New, HashTypeSHA2384: sha512.New384, HashTypeSHA2512: sha512.New, + HashTypeSHA3224: sha3.New224, + HashTypeSHA3256: sha3.New256, + HashTypeSHA3384: sha3.New384, + HashTypeSHA3512: sha3.New512, } MarshalingTypeMap = map[string]MarshalingType{ diff --git a/sdk/helper/keysutil/policy.go b/sdk/helper/keysutil/policy.go index 29fad33852c6..9ab9944b8241 100644 --- a/sdk/helper/keysutil/policy.go +++ b/sdk/helper/keysutil/policy.go @@ -1140,6 +1140,14 @@ func (p *Policy) Sign(ver int, context, input []byte, hashAlgorithm HashType, si algo = crypto.SHA384 case HashTypeSHA2512: algo = crypto.SHA512 + case HashTypeSHA3224: + algo = crypto.SHA3_224 + case HashTypeSHA3256: + algo = crypto.SHA3_256 + case HashTypeSHA3384: + algo = crypto.SHA3_384 + case HashTypeSHA3512: + algo = crypto.SHA3_512 default: return nil, errutil.InternalError{Err: "unsupported hash algorithm"} } @@ -1311,6 +1319,14 @@ func (p *Policy) VerifySignature(context, input []byte, hashAlgorithm HashType, algo = crypto.SHA384 case HashTypeSHA2512: algo = crypto.SHA512 + case HashTypeSHA3224: + algo = crypto.SHA3_224 + case HashTypeSHA3256: + algo = crypto.SHA3_256 + case HashTypeSHA3384: + algo = crypto.SHA3_384 + case HashTypeSHA3512: + algo = crypto.SHA3_512 default: return false, errutil.InternalError{Err: "unsupported hash algorithm"} } diff --git a/sdk/logical/request.go b/sdk/logical/request.go index c44b8dd5a82c..d33290e35bde 100644 --- a/sdk/logical/request.go +++ b/sdk/logical/request.go @@ -382,3 +382,9 @@ type CustomHeader struct { Name string Value string } + +type CtxKeyInFlightRequestID struct{} + +func (c CtxKeyInFlightRequestID) String() string { + return "in-flight-request-ID" +} \ No newline at end of file diff --git a/sdk/logical/response.go b/sdk/logical/response.go index 19a080c7699d..e8276c789ace 100644 --- a/sdk/logical/response.go +++ b/sdk/logical/response.go @@ -228,7 +228,7 @@ type WrappingResponseWriter interface { type StatusHeaderResponseWriter struct { wrapped http.ResponseWriter wroteHeader bool - statusCode int + StatusCode int headers map[string][]*CustomHeader } @@ -236,7 +236,7 @@ func NewStatusHeaderResponseWriter(w http.ResponseWriter, h map[string][]*Custom return &StatusHeaderResponseWriter{ wrapped: w, wroteHeader: false, - statusCode: 200, + StatusCode: 200, headers: h, } } @@ -259,7 +259,7 @@ func (w *StatusHeaderResponseWriter) Write(buf []byte) (int, error) { // statusHeaderResponseWriter struct are called the internal call to the // WriterHeader invoked from inside Write method won't change the headers. if !w.wroteHeader { - w.setCustomResponseHeaders(w.statusCode) + w.setCustomResponseHeaders(w.StatusCode) } return w.wrapped.Write(buf) @@ -268,7 +268,7 @@ func (w *StatusHeaderResponseWriter) Write(buf []byte) (int, error) { func (w *StatusHeaderResponseWriter) WriteHeader(statusCode int) { w.setCustomResponseHeaders(statusCode) w.wrapped.WriteHeader(statusCode) - w.statusCode = statusCode + w.StatusCode = statusCode // in cases where Write is called after WriteHeader, let's prevent setting // ResponseWriter headers twice w.wroteHeader = true diff --git a/sdk/logical/token.go b/sdk/logical/token.go index 0586d768ead5..b204a4a6c8dd 100644 --- a/sdk/logical/token.go +++ b/sdk/logical/token.go @@ -1,7 +1,11 @@ package logical import ( + "crypto/sha256" + "encoding/base64" "fmt" + "sort" + "strings" "time" sockaddr "github.com/hashicorp/go-sockaddr" @@ -20,13 +24,24 @@ const ( // TokenTypeBatch is a batch token TokenTypeBatch - // TokenTypeDefaultService, configured on a mount, means that if + // TokenTypeDefaultService configured on a mount, means that if // TokenTypeDefault is sent back by the mount, create Service tokens TokenTypeDefaultService - // TokenTypeDefaultBatch, configured on a mount, means that if + // TokenTypeDefaultBatch configured on a mount, means that if // TokenTypeDefault is sent back by the mount, create Batch tokens TokenTypeDefaultBatch + + // ClientIDTWEDelimiter Delimiter between the string fields used to generate a client + // ID for tokens without entities. This is the 0 character, which + // is a non-printable string. Please see unicode.IsPrint for details. + ClientIDTWEDelimiter = rune('\x00') + + // SortedPoliciesTWEDelimiter Delimiter between each policy in the sorted policies used to + // generate a client ID for tokens without entities. This is the 127 + // character, which is a non-printable string. Please see unicode.IsPrint + // for details. + SortedPoliciesTWEDelimiter = rune('\x7F') ) func (t *TokenType) UnmarshalJSON(b []byte) error { @@ -154,6 +169,46 @@ type TokenEntry struct { CubbyholeID string `json:"cubbyhole_id" mapstructure:"cubbyhole_id" structs:"cubbyhole_id" sentinel:""` } +// CreateClientID returns the client ID, and a boolean which is false if the clientID +// has an entity, and true otherwise +func (te *TokenEntry) CreateClientID() (string, bool) { + var clientIDInputBuilder strings.Builder + + // if entry has an associated entity ID, return it + if te.EntityID != "" { + return te.EntityID, false + } + + // The entry is associated with a TWE (token without entity). In this case + // we must create a client ID by calculating the following formula: + // clientID = SHA256(sorted policies + namespace) + + // Step 1: Copy entry policies to a new struct + sortedPolicies := make([]string, len(te.Policies)) + copy(sortedPolicies, te.Policies) + + // Step 2: Sort and join copied policies + sort.Strings(sortedPolicies) + for _, pol := range sortedPolicies { + clientIDInputBuilder.WriteRune(SortedPoliciesTWEDelimiter) + clientIDInputBuilder.WriteString(pol) + } + + // Step 3: Add namespace ID + clientIDInputBuilder.WriteRune(ClientIDTWEDelimiter) + clientIDInputBuilder.WriteString(te.NamespaceID) + + if clientIDInputBuilder.Len() == 0 { + return "", true + } + // Step 4: Remove the first character in the string, as it's an unnecessary delimiter + clientIDInput := clientIDInputBuilder.String()[1:] + + // Step 5: Hash the sum + hashed := sha256.Sum256([]byte(clientIDInput)) + return base64.StdEncoding.EncodeToString(hashed[:]), true +} + func (te *TokenEntry) SentinelGet(key string) (interface{}, error) { if te == nil { return nil, nil diff --git a/sdk/logical/token_test.go b/sdk/logical/token_test.go index 5499e5c5bd14..e44c707a5165 100644 --- a/sdk/logical/token_test.go +++ b/sdk/logical/token_test.go @@ -1,6 +1,8 @@ package logical import ( + "crypto/sha256" + "encoding/base64" "encoding/json" "testing" ) @@ -41,3 +43,61 @@ func TestJSONSerialization(t *testing.T) { t.Fatalf("expected %v, got %v", tt, utt) } } + +// TestCreateClientID verifies that CreateClientID uses the entity ID for a token +// entry if one exists, and creates an appropriate client ID otherwise. +func TestCreateClientID(t *testing.T) { + entry := TokenEntry{NamespaceID: "namespaceFoo", Policies: []string{"bar", "baz", "foo", "banana"}} + id, isTWE := entry.CreateClientID() + if !isTWE { + t.Fatalf("TWE token should return true value in isTWE bool") + } + expectedIDPlaintext := "banana" + string(SortedPoliciesTWEDelimiter) + "bar" + + string(SortedPoliciesTWEDelimiter) + "baz" + + string(SortedPoliciesTWEDelimiter) + "foo" + string(ClientIDTWEDelimiter) + "namespaceFoo" + + hashed := sha256.Sum256([]byte(expectedIDPlaintext)) + expectedID := base64.StdEncoding.EncodeToString(hashed[:]) + if expectedID != id { + t.Fatalf("wrong ID: expected %s, found %s", expectedID, id) + } + // Test with entityID + entry = TokenEntry{EntityID: "entityFoo", NamespaceID: "namespaceFoo", Policies: []string{"bar", "baz", "foo", "banana"}} + id, isTWE = entry.CreateClientID() + if isTWE { + t.Fatalf("token with entity should return false value in isTWE bool") + } + if id != "entityFoo" { + t.Fatalf("client ID should be entity ID") + } + + // Test without namespace + entry = TokenEntry{Policies: []string{"bar", "baz", "foo", "banana"}} + id, isTWE = entry.CreateClientID() + if !isTWE { + t.Fatalf("TWE token should return true value in isTWE bool") + } + expectedIDPlaintext = "banana" + string(SortedPoliciesTWEDelimiter) + "bar" + + string(SortedPoliciesTWEDelimiter) + "baz" + + string(SortedPoliciesTWEDelimiter) + "foo" + string(ClientIDTWEDelimiter) + + hashed = sha256.Sum256([]byte(expectedIDPlaintext)) + expectedID = base64.StdEncoding.EncodeToString(hashed[:]) + if expectedID != id { + t.Fatalf("wrong ID: expected %s, found %s", expectedID, id) + } + + // Test without policies + entry = TokenEntry{NamespaceID: "namespaceFoo"} + id, isTWE = entry.CreateClientID() + if !isTWE { + t.Fatalf("TWE token should return true value in isTWE bool") + } + expectedIDPlaintext = "namespaceFoo" + + hashed = sha256.Sum256([]byte(expectedIDPlaintext)) + expectedID = base64.StdEncoding.EncodeToString(hashed[:]) + if expectedID != id { + t.Fatalf("wrong ID: expected %s, found %s", expectedID, id) + } +} diff --git a/ui/.storybook/config.js b/ui/.storybook/config.js index 3a9556ab25be..23f4116d6c8d 100644 --- a/ui/.storybook/config.js +++ b/ui/.storybook/config.js @@ -1,6 +1,7 @@ import { configure, addParameters, addDecorator } from '@storybook/ember'; import { INITIAL_VIEWPORTS } from '@storybook/addon-viewport'; import theme from './theme.js'; +import flightIconSprite from '@hashicorp/flight-icons/svg-sprite/svg-sprite-module'; function loadStories() { // automatically import all files ending in *.stories.js @@ -18,6 +19,9 @@ addParameters({ addDecorator(storyFn => { const { template, context } = storyFn(); + // flight icon sprite must be inserted into dom for icon lookup via use element + document.getElementById('root').insertAdjacentHTML('afterbegin', flightIconSprite.trim()); + // This adds styling to the Canvas tab. const styles = { style: { diff --git a/ui/app/components/clients/dashboard.js b/ui/app/components/clients/dashboard.js index 1fba7edc239f..57e6154f22a9 100644 --- a/ui/app/components/clients/dashboard.js +++ b/ui/app/components/clients/dashboard.js @@ -13,11 +13,11 @@ export default class Dashboard extends Component { @tracked barChartSelection = false; - // Determine if we have client count data based on the current tab, - // since model is slightly different for current month vs history api + // Determine if we have client count data based on the current tab get hasClientData() { if (this.args.tab === 'current') { - return this.args.model.activity && this.args.model.activity.clients; + // Show the current numbers as long as config is on + return this.args.model.config?.enabled !== 'Off'; } return this.args.model.activity && this.args.model.activity.total; } diff --git a/ui/app/components/regex-validator.hbs b/ui/app/components/regex-validator.hbs index 18c5cf64fbeb..4b38e7a676fc 100644 --- a/ui/app/components/regex-validator.hbs +++ b/ui/app/components/regex-validator.hbs @@ -57,7 +57,7 @@ {{else}}
-
{{/if}} diff --git a/ui/app/components/status-menu.js b/ui/app/components/status-menu.js index 59bfc2bcf98b..8aa4e182e344 100644 --- a/ui/app/components/status-menu.js +++ b/ui/app/components/status-menu.js @@ -11,10 +11,9 @@ export default Component.extend({ type: 'cluster', itemTag: null, glyphName: computed('type', function() { - const glyphs = { - cluster: 'status-indicator', - user: 'user-square-outline', - }; - return glyphs[this.type]; + return { + cluster: 'circle-dot', + user: 'user', + }[this.type]; }), }); diff --git a/ui/app/components/toolbar-secret-link.js b/ui/app/components/toolbar-secret-link.js index 00395ccabd4a..08318b86f629 100644 --- a/ui/app/components/toolbar-secret-link.js +++ b/ui/app/components/toolbar-secret-link.js @@ -23,7 +23,7 @@ import { computed } from '@ember/object'; export default OuterHTML.extend({ glyph: computed('type', function() { if (this.type == 'add') { - return 'plus-plain'; + return 'plus'; } else { return 'chevron-right'; } diff --git a/ui/app/models/transit-key.js b/ui/app/models/transit-key.js index ffb203c74829..09c6f47de272 100644 --- a/ui/app/models/transit-key.js +++ b/ui/app/models/transit-key.js @@ -8,12 +8,12 @@ const ACTION_VALUES = { encrypt: { isSupported: 'supportsEncryption', description: 'Looks up wrapping properties for the given token', - glyph: 'lock-closed', + glyph: 'lock-fill', }, decrypt: { isSupported: 'supportsDecryption', description: 'Decrypts the provided ciphertext using this key', - glyph: 'envelope-unsealed--outline', + glyph: 'mail-open', }, datakey: { isSupported: 'supportsEncryption', @@ -23,20 +23,28 @@ const ACTION_VALUES = { rewrap: { isSupported: 'supportsEncryption', description: 'Rewraps the ciphertext using the latest version of the named key', - glyph: 'refresh-default', + glyph: 'reload', }, sign: { isSupported: 'supportsSigning', description: 'Get the cryptographic signature of the given data', - glyph: 'edit', + glyph: 'pencil-tool', + }, + hmac: { + isSupported: true, + description: 'Generate a data digest using a hash algorithm', + glyph: 'shuffle', }, - hmac: { isSupported: true, description: 'Generate a data digest using a hash algorithm', glyph: 'remix' }, verify: { isSupported: true, description: 'Validate the provided signature for the given data', - glyph: 'check-circle-outline', + glyph: 'check-circle', + }, + export: { + isSupported: 'exportable', + description: 'Get the named key', + glyph: 'external-link', }, - export: { isSupported: 'exportable', description: 'Get the named key', glyph: 'exit' }, }; export default Model.extend({ diff --git a/ui/app/routes/vault/cluster.js b/ui/app/routes/vault/cluster.js index ce4d17ff0014..c9b1c150f4d8 100644 --- a/ui/app/routes/vault/cluster.js +++ b/ui/app/routes/vault/cluster.js @@ -40,8 +40,8 @@ export default Route.extend(ModelBoundaryRoute, ClusterRoute, { const currentTokenName = this.auth.get('currentTokenName'); // if no namespace queryParam and user authenticated, // use user's root namespace to redirect to properly param'd url - if (this.featureFlagService.managedNamespaceRoot && !this.version.hasNamespaces) { - window.alert('Cannot use Cloud Admin Namespace flag with OSS Vault'); + if (this.featureFlagService.managedNamespaceRoot && this.version.isOss) { + console.error('Cannot use Cloud Admin Namespace flag with OSS Vault'); } if (!namespace && currentTokenName && !Ember.testing) { const storage = getStorage().getItem(currentTokenName); diff --git a/ui/app/styles/components/console-ui-panel.scss b/ui/app/styles/components/console-ui-panel.scss index 170af92dec9b..1542dcaec7ad 100644 --- a/ui/app/styles/components/console-ui-panel.scss +++ b/ui/app/styles/components/console-ui-panel.scss @@ -106,7 +106,7 @@ margin-left: calc(#{$console-spacing} - 0.33rem); position: relative; - .hs-icon { + svg { position: absolute; left: 0; top: 0; diff --git a/ui/app/styles/components/hs-icon.scss b/ui/app/styles/components/icon.scss similarity index 84% rename from ui/app/styles/components/hs-icon.scss rename to ui/app/styles/components/icon.scss index 80507831ec47..e60da0533798 100644 --- a/ui/app/styles/components/hs-icon.scss +++ b/ui/app/styles/components/icon.scss @@ -43,3 +43,10 @@ width: 32px; height: 32px; } + +.flight-icon { + &.flight-icon-display-inline { + vertical-align: middle; + margin: 0px 4px; + } +} diff --git a/ui/app/styles/core.scss b/ui/app/styles/core.scss index 6ec797e43dda..7d91d8efd9af 100644 --- a/ui/app/styles/core.scss +++ b/ui/app/styles/core.scss @@ -121,4 +121,4 @@ @import './components/vlt-table'; // bulma-free-zone -@import './components/hs-icon'; +@import './components/icon'; diff --git a/ui/app/templates/components/alert-popup.hbs b/ui/app/templates/components/alert-popup.hbs index c6d73c1fa9ef..389c1eeaa065 100644 --- a/ui/app/templates/components/alert-popup.hbs +++ b/ui/app/templates/components/alert-popup.hbs @@ -3,16 +3,12 @@
{{type.text}} diff --git a/ui/app/templates/components/clients/dashboard.hbs b/ui/app/templates/components/clients/dashboard.hbs index ec5d327747ac..845c4a2fcb2d 100644 --- a/ui/app/templates/components/clients/dashboard.hbs +++ b/ui/app/templates/components/clients/dashboard.hbs @@ -1,4 +1,4 @@ -{{#if (eq @model.config.queriesAvailable false)}} +{{#if (and (eq @tab 'history') (eq @model.config.queriesAvailable false))}} {{#if (eq @model.config.enabled 'On')}} @@ -116,7 +116,7 @@ @@ -125,7 +125,7 @@ diff --git a/ui/app/templates/components/cluster-info.hbs b/ui/app/templates/components/cluster-info.hbs index c404841d7f39..17e3ff1ca7f6 100644 --- a/ui/app/templates/components/cluster-info.hbs +++ b/ui/app/templates/components/cluster-info.hbs @@ -27,7 +27,7 @@
Enable - +
@@ -66,14 +66,14 @@
Unsealed - +
{{else}}
Unsealed - +
{{/if}} @@ -81,7 +81,7 @@
Sealed - +
{{/if}} diff --git a/ui/app/templates/components/console/command-input.hbs b/ui/app/templates/components/console/command-input.hbs index ab4be479e1e5..4d666b3507e6 100644 --- a/ui/app/templates/components/console/command-input.hbs +++ b/ui/app/templates/components/console/command-input.hbs @@ -7,7 +7,10 @@ - +
diff --git a/ui/app/templates/components/console/log-command.hbs b/ui/app/templates/components/console/log-command.hbs index cc446877d8dd..87700e2e2530 100644 --- a/ui/app/templates/components/console/log-command.hbs +++ b/ui/app/templates/components/console/log-command.hbs @@ -1,2 +1,2 @@ {{!-- using Icon here instead of Chevron because two nested tagless components results in a rendered line break between the tags breaking the layout in the
 --}}
-
+
{{content}}
diff --git a/ui/app/templates/components/console/log-error-with-html.hbs b/ui/app/templates/components/console/log-error-with-html.hbs index 45e0c4540d24..bff73bf39d8e 100644 --- a/ui/app/templates/components/console/log-error-with-html.hbs +++ b/ui/app/templates/components/console/log-error-with-html.hbs @@ -1,5 +1,5 @@ {{! template-lint-disable no-triple-curlies}}
-
diff --git a/ui/app/templates/components/console/log-error.hbs b/ui/app/templates/components/console/log-error.hbs index 11cd2647b2a5..60a2dad428de 100644 --- a/ui/app/templates/components/console/log-error.hbs +++ b/ui/app/templates/components/console/log-error.hbs @@ -1,4 +1,4 @@
-
diff --git a/ui/app/templates/components/console/log-help.hbs b/ui/app/templates/components/console/log-help.hbs index 6d9aa8419fcd..09e6c3cb1cf2 100644 --- a/ui/app/templates/components/console/log-help.hbs +++ b/ui/app/templates/components/console/log-help.hbs @@ -1,5 +1,5 @@
-
{{/each}} diff --git a/ui/app/templates/components/wizard-section.hbs b/ui/app/templates/components/wizard-section.hbs index 9871432428b0..20f2166f4e54 100644 --- a/ui/app/templates/components/wizard-section.hbs +++ b/ui/app/templates/components/wizard-section.hbs @@ -1,7 +1,7 @@

{{#if headerIcon}} -

@@ -14,7 +14,7 @@ {{/if}} {{#if docText}} - {{/if}}
diff --git a/ui/app/templates/components/wizard/ad-engine.hbs b/ui/app/templates/components/wizard/ad-engine.hbs index 027e078c725d..492b51a4ef48 100644 --- a/ui/app/templates/components/wizard/ad-engine.hbs +++ b/ui/app/templates/components/wizard/ad-engine.hbs @@ -1,6 +1,6 @@ diff --git a/ui/app/templates/components/wizard/alicloud-engine.hbs b/ui/app/templates/components/wizard/alicloud-engine.hbs index 41e25e764b57..bf23863f166d 100644 --- a/ui/app/templates/components/wizard/alicloud-engine.hbs +++ b/ui/app/templates/components/wizard/alicloud-engine.hbs @@ -1,6 +1,6 @@ diff --git a/ui/app/templates/components/wizard/alicloud-method.hbs b/ui/app/templates/components/wizard/alicloud-method.hbs index 0f4c92c23d6d..383f059cadc8 100644 --- a/ui/app/templates/components/wizard/alicloud-method.hbs +++ b/ui/app/templates/components/wizard/alicloud-method.hbs @@ -1,6 +1,6 @@ diff --git a/ui/app/templates/components/wizard/approle-method.hbs b/ui/app/templates/components/wizard/approle-method.hbs index c47898c687c2..a058962dc3a6 100644 --- a/ui/app/templates/components/wizard/approle-method.hbs +++ b/ui/app/templates/components/wizard/approle-method.hbs @@ -1,6 +1,6 @@ diff --git a/ui/app/templates/components/wizard/auth-details.hbs b/ui/app/templates/components/wizard/auth-details.hbs index d0514a48e2f5..061fc631e548 100644 --- a/ui/app/templates/components/wizard/auth-details.hbs +++ b/ui/app/templates/components/wizard/auth-details.hbs @@ -12,7 +12,7 @@ @class="wizard-details" > {{#if selectedFeatures}} - + + About {{estimatedTime}} minutes + {{/if}} diff --git a/ui/app/templates/components/wizard/gcp-engine.hbs b/ui/app/templates/components/wizard/gcp-engine.hbs index e61ced1f421a..89cf14d8d9d9 100644 --- a/ui/app/templates/components/wizard/gcp-engine.hbs +++ b/ui/app/templates/components/wizard/gcp-engine.hbs @@ -1,6 +1,6 @@ diff --git a/ui/app/templates/components/wizard/gcp-method.hbs b/ui/app/templates/components/wizard/gcp-method.hbs index 36869babcac9..625b7c957957 100644 --- a/ui/app/templates/components/wizard/gcp-method.hbs +++ b/ui/app/templates/components/wizard/gcp-method.hbs @@ -1,6 +1,6 @@ diff --git a/ui/app/templates/components/wizard/gcpkms-engine.hbs b/ui/app/templates/components/wizard/gcpkms-engine.hbs index 18fc848198be..6004b65e703b 100644 --- a/ui/app/templates/components/wizard/gcpkms-engine.hbs +++ b/ui/app/templates/components/wizard/gcpkms-engine.hbs @@ -1,6 +1,6 @@ diff --git a/ui/app/templates/components/wizard/kmip-engine.hbs b/ui/app/templates/components/wizard/kmip-engine.hbs index aed2bc6a7b36..3c743c6f49f2 100644 --- a/ui/app/templates/components/wizard/kmip-engine.hbs +++ b/ui/app/templates/components/wizard/kmip-engine.hbs @@ -1,4 +1,4 @@ -

The KMIP secrets engine allows Vault to act as a KMIP server provider and handle the lifecycle of KMIP managed diff --git a/ui/app/templates/components/wizard/kubernetes-method.hbs b/ui/app/templates/components/wizard/kubernetes-method.hbs index fe39cec59478..2fa1cc349037 100644 --- a/ui/app/templates/components/wizard/kubernetes-method.hbs +++ b/ui/app/templates/components/wizard/kubernetes-method.hbs @@ -1,6 +1,6 @@ diff --git a/ui/app/templates/components/wizard/kv-engine.hbs b/ui/app/templates/components/wizard/kv-engine.hbs index c03a8d17cbfe..ed73c82b8fd3 100644 --- a/ui/app/templates/components/wizard/kv-engine.hbs +++ b/ui/app/templates/components/wizard/kv-engine.hbs @@ -1,6 +1,6 @@ diff --git a/ui/app/templates/components/wizard/ldap-method.hbs b/ui/app/templates/components/wizard/ldap-method.hbs index 1855694d5f4c..356720b04ce5 100644 --- a/ui/app/templates/components/wizard/ldap-method.hbs +++ b/ui/app/templates/components/wizard/ldap-method.hbs @@ -1,6 +1,6 @@ diff --git a/ui/app/templates/components/wizard/okta-method.hbs b/ui/app/templates/components/wizard/okta-method.hbs index 1440bebe5a55..b61ec473a26b 100644 --- a/ui/app/templates/components/wizard/okta-method.hbs +++ b/ui/app/templates/components/wizard/okta-method.hbs @@ -1,6 +1,6 @@ diff --git a/ui/app/templates/components/wizard/pki-engine.hbs b/ui/app/templates/components/wizard/pki-engine.hbs index 73a9d21293c9..f847aa46ea52 100644 --- a/ui/app/templates/components/wizard/pki-engine.hbs +++ b/ui/app/templates/components/wizard/pki-engine.hbs @@ -1,6 +1,6 @@ diff --git a/ui/app/templates/components/wizard/radius-method.hbs b/ui/app/templates/components/wizard/radius-method.hbs index 6df40fab1885..cb0d5b9fff55 100644 --- a/ui/app/templates/components/wizard/radius-method.hbs +++ b/ui/app/templates/components/wizard/radius-method.hbs @@ -1,6 +1,6 @@ diff --git a/ui/app/templates/components/wizard/replication-setup.hbs b/ui/app/templates/components/wizard/replication-setup.hbs index 449ac22b0b33..1e9b1944e5e9 100644 --- a/ui/app/templates/components/wizard/replication-setup.hbs +++ b/ui/app/templates/components/wizard/replication-setup.hbs @@ -10,11 +10,12 @@

- - diff --git a/ui/app/templates/components/wizard/secrets-display.hbs b/ui/app/templates/components/wizard/secrets-display.hbs index fa895b5f57db..a907ca56ef68 100644 --- a/ui/app/templates/components/wizard/secrets-display.hbs +++ b/ui/app/templates/components/wizard/secrets-display.hbs @@ -26,11 +26,11 @@ > {{#if @isSupported}} {{/if}}
diff --git a/ui/app/templates/components/wizard/tutorial-error.hbs b/ui/app/templates/components/wizard/tutorial-error.hbs index 12291f863d85..fa5d1974b4d8 100644 --- a/ui/app/templates/components/wizard/tutorial-error.hbs +++ b/ui/app/templates/components/wizard/tutorial-error.hbs @@ -7,7 +7,7 @@

Want a tour? Our helpful guide will introduce you to the Vault Web UI.

diff --git a/ui/app/templates/components/wizard/tutorial-paused.hbs b/ui/app/templates/components/wizard/tutorial-paused.hbs index b8f91238492e..1088b3f994a0 100644 --- a/ui/app/templates/components/wizard/tutorial-paused.hbs +++ b/ui/app/templates/components/wizard/tutorial-paused.hbs @@ -5,7 +5,7 @@ @hidePopup={{true}} >

Feel free to explore Vault. Click below to get back to the guide or close this window.

diff --git a/ui/app/templates/components/wizard/userpass-method.hbs b/ui/app/templates/components/wizard/userpass-method.hbs index df11cf5642f6..d10bc573a927 100644 --- a/ui/app/templates/components/wizard/userpass-method.hbs +++ b/ui/app/templates/components/wizard/userpass-method.hbs @@ -1,6 +1,6 @@ diff --git a/ui/app/templates/vault.hbs b/ui/app/templates/vault.hbs index e9e36f17996e..f30203ba9f0b 100644 --- a/ui/app/templates/vault.hbs +++ b/ui/app/templates/vault.hbs @@ -3,10 +3,10 @@
@@ -32,7 +32,7 @@ {{#if (eq env "development") }}
- Local development + Local development
{{/if}} diff --git a/ui/app/templates/vault/cluster.hbs b/ui/app/templates/vault/cluster.hbs index ab20e6f0a4d0..0e2deb1e894b 100644 --- a/ui/app/templates/vault/cluster.hbs +++ b/ui/app/templates/vault/cluster.hbs @@ -3,7 +3,7 @@ as |Nav|> - + @@ -89,7 +89,7 @@ {{/if}} {{else}} - + {{/if}}
@@ -33,25 +33,21 @@ {{else if valueIsBoolean}} {{#if value}}
diff --git a/ui/lib/core/addon/templates/components/modal.hbs b/ui/lib/core/addon/templates/components/modal.hbs index de836e797874..ef491ffd0ceb 100644 --- a/ui/lib/core/addon/templates/components/modal.hbs +++ b/ui/lib/core/addon/templates/components/modal.hbs @@ -6,10 +6,9 @@
diff --git a/ui/lib/core/addon/templates/components/popup-menu.hbs b/ui/lib/core/addon/templates/components/popup-menu.hbs index 2c88218246fc..6ac7769504ef 100644 --- a/ui/lib/core/addon/templates/components/popup-menu.hbs +++ b/ui/lib/core/addon/templates/components/popup-menu.hbs @@ -1,7 +1,7 @@ {{#basic-dropdown class="popup-menu" horizontalPosition="auto-right" verticalPosition="below" onOpen=onOpen as |d|}} {{#d.trigger tagName="button" class=(concat "popup-menu-trigger button is-ghost" (if d.isOpen " is-active")) data-test-popup-menu-trigger=true}} diff --git a/ui/lib/core/addon/templates/components/replication-dashboard.hbs b/ui/lib/core/addon/templates/components/replication-dashboard.hbs index feeb6222bfb3..5bb4c6ab5aed 100644 --- a/ui/lib/core/addon/templates/components/replication-dashboard.hbs +++ b/ui/lib/core/addon/templates/components/replication-dashboard.hbs @@ -21,7 +21,11 @@ {{/unless}}

{{summaryState}} {{#if (get (cluster-states summaryState) "isOk")}} - + {{/if}}

diff --git a/ui/lib/core/addon/templates/components/replication-mode-summary.hbs b/ui/lib/core/addon/templates/components/replication-mode-summary.hbs index 014897c334ab..7a9c79f9d8a1 100644 --- a/ui/lib/core/addon/templates/components/replication-mode-summary.hbs +++ b/ui/lib/core/addon/templates/components/replication-mode-summary.hbs @@ -34,10 +34,7 @@ {{#if replicationEnabled}} {{#if (cluster-states modeState)}} - {{else if syncProgress}} @@ -46,8 +43,8 @@ {{/if}} {{else}} {{/if}} diff --git a/ui/lib/core/addon/templates/components/replication-secondary-card.hbs b/ui/lib/core/addon/templates/components/replication-secondary-card.hbs index 22cebdb7913f..d906e08005c7 100644 --- a/ui/lib/core/addon/templates/components/replication-secondary-card.hbs +++ b/ui/lib/core/addon/templates/components/replication-secondary-card.hbs @@ -53,7 +53,7 @@ {{#if inSyncState}} - +
@@ -87,7 +87,7 @@ {{/if}} diff --git a/ui/lib/core/addon/templates/components/search-select-placeholder.hbs b/ui/lib/core/addon/templates/components/search-select-placeholder.hbs index 6a5c308c8b78..e13e318b826b 100644 --- a/ui/lib/core/addon/templates/components/search-select-placeholder.hbs +++ b/ui/lib/core/addon/templates/components/search-select-placeholder.hbs @@ -1,6 +1,6 @@

Search -

diff --git a/ui/lib/core/addon/templates/components/search-select.hbs b/ui/lib/core/addon/templates/components/search-select.hbs index 6cccd18d20f3..787a0d3f564a 100644 --- a/ui/lib/core/addon/templates/components/search-select.hbs +++ b/ui/lib/core/addon/templates/components/search-select.hbs @@ -67,7 +67,7 @@
diff --git a/ui/lib/core/addon/templates/components/string-list.hbs b/ui/lib/core/addon/templates/components/string-list.hbs index 536778e72e2e..1e3eb4880c86 100644 --- a/ui/lib/core/addon/templates/components/string-list.hbs +++ b/ui/lib/core/addon/templates/components/string-list.hbs @@ -39,7 +39,7 @@ data-test-string-list-button="delete" {{action "removeInput" index}} > - + {{/if}}
diff --git a/ui/lib/core/addon/templates/components/toggle-button.hbs b/ui/lib/core/addon/templates/components/toggle-button.hbs index 529a4b3171ea..9894156de6ac 100644 --- a/ui/lib/core/addon/templates/components/toggle-button.hbs +++ b/ui/lib/core/addon/templates/components/toggle-button.hbs @@ -1,5 +1,5 @@ {{#if isOpen}} - {{openLabel}} + {{openLabel}} {{else}} - {{closedLabel}} + {{closedLabel}} {{/if}} diff --git a/ui/lib/core/addon/templates/components/toolbar-link.hbs b/ui/lib/core/addon/templates/components/toolbar-link.hbs index 50d3e7995bf0..c00a50f43824 100644 --- a/ui/lib/core/addon/templates/components/toolbar-link.hbs +++ b/ui/lib/core/addon/templates/components/toolbar-link.hbs @@ -19,7 +19,7 @@ - {{yield}} + {{yield}} @@ -30,7 +30,7 @@ {{else}} - {{yield}} + {{yield}} {{/if}} {{/let}} diff --git a/ui/lib/core/addon/templates/components/ttl-form.hbs b/ui/lib/core/addon/templates/components/ttl-form.hbs index 391023d473e1..38fd76eb1515 100644 --- a/ui/lib/core/addon/templates/components/ttl-form.hbs +++ b/ui/lib/core/addon/templates/components/ttl-form.hbs @@ -26,12 +26,7 @@ {{#if errorMessage}}
-
{{errorMessage}} diff --git a/ui/lib/core/addon/templates/components/ttl-picker2.hbs b/ui/lib/core/addon/templates/components/ttl-picker2.hbs index 5bb299fb3d21..252d74924f6a 100644 --- a/ui/lib/core/addon/templates/components/ttl-picker2.hbs +++ b/ui/lib/core/addon/templates/components/ttl-picker2.hbs @@ -12,7 +12,7 @@ {{#if description}} - +
@@ -53,10 +53,8 @@
diff --git a/ui/lib/core/icon-mappings.js b/ui/lib/core/icon-mappings.js new file mode 100644 index 000000000000..021cbb30b7e8 --- /dev/null +++ b/ui/lib/core/icon-mappings.js @@ -0,0 +1,222 @@ +// icons that exist in the public folder which are not part of the Structure set +// values represent match to icon in Flight set with null representing no match +export const localIconMap = { + hashicorp: null, + loop: 'sync', + reply: 'corner-up-left', + console: 'terminal-screen', + pki: 'file-text', + replication: 'replication-direct', + 'perf-replication': 'replication-perf', + 'status-indicator': 'circle-dot', + tour: null, + approle: 'cpu', + cert: 'certificate', + consul: null, + gcpkms: 'gcp-color', + kmip: 'unlock', + kv: 'key-values', + ldap: 'user', + okta: 'okta-color', + radius: 'user', + ssh: 'terminal-screen', + totp: 'history', + transit: 'swap-horizontal', + userpass: 'identity-user', + stopwatch: 'clock', + 'vault-logo': null, + auth: 'user', + 'android-sync': 'sync-reverse', +}; +// complete list of Structure icons mapped to their Flight counterpart +// null values represent no direct correlation to icon in Flight set +// Flight icon lookup @ https://flight-hashicorp.vercel.app/ +export const structureIconMap = { + 'alert-circle-fill': 'alert-circle-fill', + 'alert-circle-outline': 'alert-circle', + 'alert-triangle': 'alert-triangle-fill', + 'arrow-down': 'arrow-down', + 'arrow-left': 'arrow-left', + 'arrow-right': 'arrow-right', + 'arrow-up': 'arrow-up', + bolt: 'zap', + 'box-check-fill': 'check-square-fill', + 'box-outline': 'square', + broadcast: 'radio', + bug: 'bug', + calendar: 'calendar', + 'cancel-circle-fill': 'x-circle-fill', + 'cancel-circle-outline': 'x-circle', + 'cancel-plain': 'x', + 'cancel-square-fill': 'x-square-fill', + 'cancel-square-outline': 'x-square', + 'caret-down': null, + 'caret-up': null, + 'check-circle-fill': 'check-circle-fill', + 'check-circle-outline': 'check-circle', + 'check-plain': 'check', + 'chevron-down': 'chevron-down', + 'chevron-left': 'chevron-left', + 'chevron-right': 'chevron-right', + 'chevron-up': 'chevron-up', + 'clock-fill': null, + 'clock-outline': 'clock', + 'cloud-fail': 'cloud-x', + code: 'code', + console: 'terminal', + 'copy-action': 'clipboard-copy', + 'copy-success': 'clipboard-checked', + database: 'database', + delay: 'delay', + 'deny-alt': null, + 'deny-default': null, + disabled: 'skip', + docs: 'docs-link', + dot: 'circle-fill', + download: 'download', + edit: 'pencil-tool', + 'envelope-sealed-fill': null, + 'envelope-sealed-outline': 'mail', + 'envelope-unsealed--outline': 'mail-open', + 'envelope-unsealed-fill': null, + exit: 'external-link', + 'expand-less': 'minimize', + 'expand-more': 'maximize', + 'file-error': 'file-x', + 'file-fill': 'file-text', + 'file-outline': 'file', + 'file-success': 'file-check', + filter: 'filter', + flag: 'flag', + 'folder-fill': 'folder-fill', + 'folder-outline': 'folder', + gateway: 'gateway', + 'gift-fill': null, + 'gift-outline': 'gift', + 'git-branch': 'git-branch', + 'git-commit': 'git-commit', + 'git-pull-request': 'git-pull-request', + 'git-repository': 'git-repo', + guide: 'guide', + health: 'activity', + 'help-circle-fill': null, + 'help-circle-outline': 'help', + history: 'history', + 'info-circle-fill': null, + 'info-circle-outline': 'info', + key: 'key', + layers: 'layers', + leader: 'star-circle', + learn: 'learn-link', + link: 'link', + loading: '', + 'lock-closed-fill': 'lock-fill', + 'lock-closed-outline': 'lock', + 'lock-closed': 'lock-fill', + 'lock-disabled': 'lock-disabled', + 'lock-open-outline': 'unlock', + 'lock-open': 'unlock', + 'logo-aws-color': 'aws-color', + 'logo-aws-monochrome': 'aws', + 'logo-alicloud-color': 'alibaba-color', + 'logo-alicloud-monochrome': 'alibaba', + 'logo-auth0-color': 'auth0-color', + 'logo-auth0-monochrome': 'auth0', + 'logo-azure-color': 'azure-color', + 'logo-azure-monochrome': 'azure', + 'logo-azure-dev-ops-color': 'azure-devops-color', + 'logo-azure-dev-ops-monochrome': 'azure-devops', + 'logo-bitbucket-color': 'bitbucket-color', + 'logo-bitbucket-monochrome': 'bitbucket', + 'logo-f5-color': 'f5-color', + 'logo-f5-monochrome': 'f5', + 'logo-gcp-color': 'gcp-color', + 'logo-gcp-monochrome': 'gcp', + 'logo-github-color': 'github-color', + 'logo-github-monochrome': 'github', + 'logo-gitlab-color': 'gitlab-color', + 'logo-gitlab-monochrome': 'gitlab', + 'logo-google-color': 'google-color', + 'logo-google-monochrome': 'google', + 'logo-kubernetes-color': 'kubernetes-color', + 'logo-kubernetes-monochrome': 'kubernetes', + 'logo-microsoft-color': 'microsoft-color', + 'logo-microsoft-monochrome': 'microsoft', + 'logo-okta-color': 'okta-color', + 'logo-okta-monochrome': 'okta', + 'logo-oracle-color': 'oracle-color', + 'logo-oracle-monochrome': 'oracle', + 'logo-slack-color': 'slack-color', + 'logo-slack-monochrome': 'slack', + 'logo-vmware-color': 'vmware-color', + 'logo-vmware-monochrome': 'vmware', + menu: 'menu', + mesh: 'mesh', + 'message-fill': 'message-square-fill', + 'message-outline': 'message-square-fill', + message: 'message-square-fill', + 'minus-circle-fill': null, + 'minus-circle-outline': 'minus-circle', + 'minus-plain': 'minus', + 'minus-square-fill': 'minus-square', + module: 'module', + 'more-horizontal': 'more-horizontal', + 'more-vertical': 'more-vertical', + network: 'network', + 'notification-disabled': 'notification-disabled', + 'notification-fill': 'notification-fill', + 'notification-outline': 'bell', + outline: 'outline', + 'page-outline': 'outline', + path: 'path', + 'play-fill': 'play-circle', + 'play-outline': 'play-circle', + 'play-plain': 'play', + 'plus-circle-fill': null, + 'plus-circle-outline': 'plus-circle', + 'plus-plain': 'plus', + 'plus-square-fill': 'plus-square', + provider: 'provider', + 'public-default': 'globe', + 'public-locked': 'globe-private', + queue: 'queue', + 'radio-button-checked': 'circle-dot', + 'radio-button-unchecked': 'circle', + random: 'random', + redirect: 'redirect', + 'refresh-alert': 'refresh-alert', + 'refresh-default': 'reload', + remix: 'shuffle', + ribbon: 'award', + run: '', + search: 'search', + server: 'server', + settings: 'settings', + sort: 'sort-desc', + 'source-file': 'file-source', + 'star-fill': 'star-fill', + 'star-outline': 'star', + 'sub-left': 'corner-down-left', + 'sub-right': 'corner-down-right', + support: 'support', + 'swap-horizontal': 'swap-horizontal', + 'swap-vertical': 'swap-vertical', + syncing: 'syncing', + tag: 'tag', + tokens: 'token', + trash: 'trash', + tune: 'sliders', + 'unfold-less': 'unfold-close', + 'unfold-more': 'unfold-open', + upload: 'upload', + 'user-add': 'user-plus', + 'user-organization': 'org', + 'user-plain': 'user', + 'user-square-fill': 'user-circle-fill', + 'user-square-outline': 'user-circle', + 'user-team': 'users', + 'visibility-hide': 'eye-off', + 'visibility-show': 'eye', + webhook: 'webhook', + partner: 'users', +}; diff --git a/ui/lib/core/package.json b/ui/lib/core/package.json index 38f8f4cb1c46..b0ddc13f622f 100644 --- a/ui/lib/core/package.json +++ b/ui/lib/core/package.json @@ -24,6 +24,8 @@ "ember-svg-jar": "*", "ember-truth-helpers": "*", "ember-wormhole": "^0.5.5", - "escape-string-regexp": "*" + "escape-string-regexp": "*", + "@hashicorp/ember-flight-icons": "*", + "@hashicorp/flight-icons": "*" } } diff --git a/ui/lib/core/stories/icon.md b/ui/lib/core/stories/icon.md index 52cf72045a8a..56187c9dde1b 100644 --- a/ui/lib/core/stories/icon.md +++ b/ui/lib/core/stories/icon.md @@ -3,17 +3,19 @@ ## Icon `Icon` components are glyphs used to indicate important information. +Flight icon documentation at https://flight-hashicorp.vercel.app/ + **Params** | Param | Type | Default | Description | | --- | --- | --- | --- | -| glyph | String | | The name of the SVG to render inline. | -| [size] | String | 'm' | The size of the Icon, can be one of 's', 'm', 'l', 'xlm', 'xl', 'xxl'. The default is 'm'. | +| name | string | null | The name of the SVG to render inline. | +| [size] | string | 16 | size for flight icon, can be 16 or 24 | **Example** ```js - + ``` **See** diff --git a/ui/lib/core/stories/icon.stories.js b/ui/lib/core/stories/icon.stories.js index eed77a9f7b4e..929e042eef51 100644 --- a/ui/lib/core/stories/icon.stories.js +++ b/ui/lib/core/stories/icon.stories.js @@ -3,6 +3,7 @@ import { storiesOf } from '@storybook/ember'; import notes from './icon.md'; import icons from '../../../node_modules/@hashicorp/structure-icons/dist/index.js'; import { withKnobs, select } from '@storybook/addon-knobs'; +import { structureIconMap, localIconMap } from '../icon-mappings'; storiesOf('Icon', module) .addParameters({ options: { showPanel: true } }) @@ -11,31 +12,96 @@ storiesOf('Icon', module) 'Icon', () => ({ template: hbs` -
Icons from HashiCorp Structure
+
HashiCorp Flight Icons
+ https://flight-hashicorp.vercel.app/ + +
+ HashiCorp Structure Icons with Flight Mappings +
- - + + + + {{#each types as |type|}} + {{#let (get structureIconMap type) as |flightIcon|}} + + + {{/let}} {{/each}}
Glyph titleGlyphStructure Icon NameStructure GlyphFlight Icon NameFlight Glyph>
-
{{humanize type}}
+ {{type}}
- + + {{#if flightIcon}} + {{flightIcon}} + {{else}} + — + {{/if}} + + {{#if flightIcon}} + + {{else}} + — + {{/if}} +
+ +
+ Local Icons with Flight Mappings +
+ + + + + + + + + + + {{#each-in localIconMap as |localIcon flightIcon|}} + + + + + + + {{/each-in}} + +
Local Icon NameGlyphFlight Icon NameFlight Glyph
+ {{localIcon}} + + + + {{#if flightIcon}} + {{flightIcon}} + {{else}} + — + {{/if}} + + {{#if flightIcon}} + + {{else}} + — + {{/if}} +
`, context: { types: icons, - size: select('Size', ['s', 'm', 'l', 'xl', 'xxl'], 'm'), + structureIconMap, + localIconMap, + size: select('Size', ['16', '24'], '16'), }, }), { notes } diff --git a/ui/lib/core/stories/toolbar/toolbar-filters.md b/ui/lib/core/stories/toolbar/toolbar-filters.md index 6f95cc3d1903..5c8e8be4bf90 100644 --- a/ui/lib/core/stories/toolbar/toolbar-filters.md +++ b/ui/lib/core/stories/toolbar/toolbar-filters.md @@ -1,9 +1,10 @@ - + ## ToolbarFilters `ToolbarFilters` components are containers for Toolbar filters and toggles. It should only be used inside of `Toolbar`. +**Params** **Example** ```js @@ -11,7 +12,7 @@ It should only be used inside of `Toolbar`.
- +
@@ -20,6 +21,6 @@ It should only be used inside of `Toolbar`. **See** - [Uses of ToolbarFilters](https://github.com/hashicorp/vault/search?l=Handlebars&q=ToolbarFilters+OR+toolbar-filters) -- [ToolbarFilters Source Code](https://github.com/hashicorp/vault/blob/master/ui/app/components/toolbar-filters.js) +- [ToolbarFilters Source Code](https://github.com/hashicorp/vault/blob/master/ui/lib/core/addon/components/toolbar-filters.js) --- diff --git a/ui/lib/core/stories/toolbar/toolbar-filters.stories.js b/ui/lib/core/stories/toolbar/toolbar-filters.stories.js index a790bd6b1556..f9165161c5e3 100644 --- a/ui/lib/core/stories/toolbar/toolbar-filters.stories.js +++ b/ui/lib/core/stories/toolbar/toolbar-filters.stories.js @@ -15,7 +15,7 @@ storiesOf('Toolbar', module)
- +
diff --git a/ui/lib/core/stories/toolbar/toolbar.stories.js b/ui/lib/core/stories/toolbar/toolbar.stories.js index 18bdc7299c71..72ef734627b6 100644 --- a/ui/lib/core/stories/toolbar/toolbar.stories.js +++ b/ui/lib/core/stories/toolbar/toolbar.stories.js @@ -17,7 +17,7 @@ storiesOf('Toolbar', module)
- +
diff --git a/ui/lib/kmip/addon/templates/components/header-scope.hbs b/ui/lib/kmip/addon/templates/components/header-scope.hbs index 24191dc924be..4261ffdc1ee8 100644 --- a/ui/lib/kmip/addon/templates/components/header-scope.hbs +++ b/ui/lib/kmip/addon/templates/components/header-scope.hbs @@ -5,8 +5,8 @@

{{this.secretMountPath.currentPath}} diff --git a/ui/lib/kmip/addon/templates/components/operation-field-display.hbs b/ui/lib/kmip/addon/templates/components/operation-field-display.hbs index b41f2d074c3c..735b52182a63 100644 --- a/ui/lib/kmip/addon/templates/components/operation-field-display.hbs +++ b/ui/lib/kmip/addon/templates/components/operation-field-display.hbs @@ -12,7 +12,7 @@

diff --git a/ui/lib/replication/addon/templates/components/path-filter-config-list.hbs b/ui/lib/replication/addon/templates/components/path-filter-config-list.hbs index 15ddfe8f73f7..80f1709b7c35 100644 --- a/ui/lib/replication/addon/templates/components/path-filter-config-list.hbs +++ b/ui/lib/replication/addon/templates/components/path-filter-config-list.hbs @@ -9,10 +9,10 @@ >
+ />
Include everything @@ -43,10 +43,10 @@ >
+ />
Allow @@ -77,8 +77,8 @@ >
diff --git a/ui/lib/replication/addon/templates/components/replication-primary-card.hbs b/ui/lib/replication/addon/templates/components/replication-primary-card.hbs index 413df111d613..94e48914d8f5 100644 --- a/ui/lib/replication/addon/templates/components/replication-primary-card.hbs +++ b/ui/lib/replication/addon/templates/components/replication-primary-card.hbs @@ -14,7 +14,7 @@ unknown {{/if}} {{#if (and glyph (not hasError))}} - + {{/if}}
diff --git a/ui/lib/replication/addon/templates/components/replication-summary.hbs b/ui/lib/replication/addon/templates/components/replication-summary.hbs index 1a4ae7b05bd4..2bb5bf912dd4 100644 --- a/ui/lib/replication/addon/templates/components/replication-summary.hbs +++ b/ui/lib/replication/addon/templates/components/replication-summary.hbs @@ -36,7 +36,7 @@ {{#if initialReplicationMode}} {{#if (eq initialReplicationMode 'dr')}}

-

@@ -44,7 +44,7 @@

{{else if (eq initialReplicationMode 'performance')}}

-

{{#unless (has-feature "Performance Replication")}} @@ -70,7 +70,7 @@