diff --git a/.circleci/config.yml b/.circleci/config.yml index f9e59a9acfff9..d1bd416b053ec 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 03e04cbc2bd47..97f5d3b440721 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 6ea817b12218a..ca3f71f0d2a4c 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 b7c515910517d..a06d2f1099f30 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 5cf5d8fcf8799..092349046de1f 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 b369521d202da..7132605cdd788 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 f6ea78fcece12..cc1e3a21a1713 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 233b081ea49c6..01a50c82a0799 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 a871940d1c211..14c85eb772264 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 4e51eedba8552..f3360f52cfb57 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 54b2b3d9d0972..84c03d3dbb797 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 0000000000000..e8d0cf5fdb39c --- /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 68070e4829bde..252b5641cd4fe 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 0000000000000..25baf7f811e8a --- /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 17dd6c11b2d16..995bea5982d4f 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 e5009386f5392..1e3da0333960a 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 fcb01d07b4462..67d705a505f7c 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 aa77dcf570feb..444e8071fdba6 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 bb68d9f02d5f8..355b0738c1dd6 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 9e9ef2c173404..a9c1f426d9280 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 deb564b129260..ac683dadd17e0 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 1842e069a5135..227ff4cf49ca7 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 f26435d88ee9c..5e4c750530db6 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 a500bb2a42e45..98ce87889a723 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 30a79a40789ab..f61017919c9da 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 756dc77e559f1..108d6218bfd79 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 c32fddc99976a..04c38d2f8eb6a 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 265d63cec1984..ade69530df78b 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 40df90838b19e..072f8a265fa15 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 2785d111bd1bc..76f5875e73ff3 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 fb4ebcc5586f2..5b360beda625c 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 0000000000000..1dea358d9db9e --- /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 378b37cbc2b64..bb3d3a69ea07d 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 0000000000000..97aea1b90bd35 --- /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 0000000000000..9c3ed52dc68b4 --- /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 10bb3b890daee..67f6b2a7b5bc2 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 3496665efc9cb..0000000000000 --- 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 0000000000000..f0bced301614b --- /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 0000000000000..968f32ad86339 --- /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 0000000000000..65284ab7ba535 --- /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 0000000000000..9c2a5bb9f2c23 --- /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 0000000000000..d8f2e71a566c6 --- /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 0000000000000..6600abedf19ee --- /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 0000000000000..4861b177763b6 --- /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 2870227a30d56..5cebb729bd7dd 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 bf5daa7474d48..a3846cd414ccc 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 afd4471d0f74a..4e6a12a6c3e59 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 885b0de63ef50..7c46eb5bd6717 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 2cdba009a7298..a8b8e56010245 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 ca9a316cb3de5..b73349405daaf 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 9f2ec6656ed7f..369ec3215d66f 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 da8641ba51def..8cdd06d384087 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 cb4671ba39432..72b7b89b55854 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 bcc581a4e1089..f5a54a8cc8da7 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 a7883a618cd01..3e20367ea6e0a 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 de732873790e7..f51b8fb34b78b 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 718009b8cf419..5b8eb634af8e5 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 c478906a7928a..e79cce6e9cdb3 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 6f466ddf6d011..84abc4c8b91f8 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 106ad7dce22b6..310e6b3b4720d 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 5b2271eefc34b..cda5b733e26b0 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 3394d04f57f51..1a4894d194bcf 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 0000000000000..eda6641276f1d --- /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 4c3bcc5bf4f53..1d9522817f143 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 fa0563c53cb77..9e6374cd0dce4 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 0000000000000..4b8a99991c7a6 --- /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 02a153eb4a0f7..636566a535c56 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 b82c1ede3898d..9cefe5aaa59ff 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 a7730033b3518..4a978b2d49979 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 543809ce2bd50..2abdf38e65fb6 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 0000000000000..cd010b2af846d --- /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 0000000000000..de64d708c68f2 --- /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 7260c9cc1f994..c4617731e2823 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 f88d66c5bea02..52826c09c4aa4 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 eca04152d5382..71aa724b42cf2 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 c8500183c69cf..f49924c40c93c 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 8d47d619dc282..f7bd782a2bd13 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 59142a399a388..2a83ab8496617 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 29fad33852c65..9ab9944b8241a 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 c44b8dd5a82c5..d33290e35bdeb 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 19a080c7699d3..e8276c789ace6 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 0586d768ead5d..b204a4a6c8ddf 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 5499e5c5bd14b..e44c707a51659 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 3a9556ab25be5..23f4116d6c8dc 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 1fba7edc239f7..57e6154f22a95 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 18c5cf64fbebd..4b38e7a676fc2 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 59bfc2bcf98bc..8aa4e182e3440 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 00395ccabd4a3..08318b86f6297 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 ffb203c74829e..09c6f47de272a 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 ce4d17ff0014e..c9b1c150f4d82 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 170af92dec9b0..1542dcaec7add 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 80507831ec47a..e60da0533798a 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 6ec797e43ddac..7d91d8efd9aff 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 c6d73c1fa9eff..389c1eeaa065a 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 ec5d327747ac1..845c4a2fcb2de 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 c404841d7f39c..17e3ff1ca7f68 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 ab4be479e1e58..4d666b3507e6e 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 cc446877d8ddf..87700e2e25302 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 45e0c4540d24a..bff73bf39d8eb 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 11cd2647b2a5e..60a2dad428de1 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 6d9aa8419fcd1..09e6c3cb1cf2a 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 9871432428b02..20f2166f4e547 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 027e078c725d7..492b51a4ef489 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 41e25e764b57d..bf23863f166da 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 0f4c92c23d6d2..383f059cadc8e 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 c47898c687c2e..a058962dc3a6c 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 d0514a48e2f59..061fc631e5488 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 e61ced1f421a4..89cf14d8d9d92 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 36869babcac96..625b7c957957a 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 18fc848198be4..6004b65e703b1 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 aed2bc6a7b36c..3c743c6f49f24 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 fe39cec59478b..2fa1cc349037a 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 c03a8d17cbfe6..ed73c82b8fd3a 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 1855694d5f4cb..356720b04ce5f 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 1440bebe5a55a..b61ec473a26b1 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 73a9d21293c94..f847aa46ea52f 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 6df40fab18854..cb0d5b9fff55d 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 449ac22b0b339..1e9b1944e5e97 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 fa895b5f57db3..a907ca56ef684 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 12291f863d85e..fa5d1974b4d84 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 b8f91238492e3..1088b3f994a06 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 df11cf5642f62..d10bc573a9275 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 e9e36f17996e5..f30203ba9f0bf 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 ab20e6f0a4d02..0e2deb1e894b5 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 de836e797874d..ef491ffd0ceb0 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 2c88218246fc4..6ac7769504ef1 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 feeb6222bfb33..5bb4c6ab5aed4 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 014897c334ab6..7a9c79f9d8a10 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 22cebdb7913ff..d906e08005c78 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 6a5c308c8b782..e13e318b826bb 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 6cccd18d20f36..787a0d3f564a3 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 536778e72e2e4..1e3eb4880c865 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 529a4b3171eac..9894156de6aca 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 50d3e7995bf0b..c00a50f438242 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 391023d473e12..38fd76eb15151 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 5bb299fb3d21e..252d74924f6a5 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 0000000000000..021cbb30b7e83 --- /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 38f8f4cb1c467..b0ddc13f622f6 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 52cf72045a8ab..56187c9dde1ba 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 eed77a9f7b4eb..929e042eef51c 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 6f95cc3d1903b..5c8e8be4bf904 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 a790bd6b15567..f9165161c5e38 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 18bdc7299c712..72ef734627b6c 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 24191dc924bed..4261ffdc1ee83 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 b41f2d074c3c6..735b52182a635 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 15ddfe8f73f74..80f1709b7c35e 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 413df111d6132..94e48914d8f56 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 1a4ae7b05bd47..2bb5bf912dd48 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 @@