New upstream version 1.14.8
This commit is contained in:
commit
b599308d69
|
@ -0,0 +1,16 @@
|
|||
schema_version = 1
|
||||
|
||||
project {
|
||||
license = "MPL-2.0"
|
||||
copyright_year = 2015
|
||||
|
||||
# (OPTIONAL) A list of globs that should not have copyright/license headers.
|
||||
# Supports doublestar glob patterns for more flexibility in defining which
|
||||
# files or folders should be ignored
|
||||
header_ignore = [
|
||||
"builtin/credential/aws/pkcs7/**",
|
||||
"ui/node_modules/**",
|
||||
"enos/modules/k8s_deploy_vault/raft-config.hcl",
|
||||
"plugins/database/postgresql/scram/**"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
vendor/* linguist-vendored
|
||||
website/* linguist-documentation
|
||||
|
||||
/packagespec.mk linguist-generated
|
||||
*.ber filter=lfs diff=lfs merge=lfs -text
|
||||
*.DS_Store filter=lfs diff=lfs merge=lfs -text
|
||||
*.eot filter=lfs diff=lfs merge=lfs -text
|
||||
*.gif filter=lfs diff=lfs merge=lfs -text
|
||||
*.ico filter=lfs diff=lfs merge=lfs -text
|
||||
*.jks filter=lfs diff=lfs merge=lfs -text
|
||||
*.jpg filter=lfs diff=lfs merge=lfs -text
|
||||
*.lzma filter=lfs diff=lfs merge=lfs -text
|
||||
*.p12 filter=lfs diff=lfs merge=lfs -text
|
||||
*.pdf filter=lfs diff=lfs merge=lfs -text
|
||||
*.png filter=lfs diff=lfs merge=lfs -text
|
||||
*.snap filter=lfs diff=lfs merge=lfs -text
|
||||
*.ttf filter=lfs diff=lfs merge=lfs -text
|
||||
*.woff filter=lfs diff=lfs merge=lfs -text
|
||||
*.woff2 filter=lfs diff=lfs merge=lfs -text
|
||||
*.xz filter=lfs diff=lfs merge=lfs -text
|
||||
*.zip filter=lfs diff=lfs merge=lfs -text
|
||||
bin/codechecker filter=lfs diff=lfs merge=lfs -text
|
||||
Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/.keystore filter=lfs diff=lfs merge=lfs -text
|
||||
Godeps/_workspace/src/github.com/gocql/gocql/testdata/pki/.truststore filter=lfs diff=lfs merge=lfs -text
|
||||
plugins/database/cassandra/test-fixtures/with_tls/stores/keystore filter=lfs diff=lfs merge=lfs -text
|
||||
plugins/database/cassandra/test-fixtures/with_tls/stores/truststore filter=lfs diff=lfs merge=lfs -text
|
|
@ -0,0 +1,5 @@
|
|||
# Code of Conduct
|
||||
|
||||
HashiCorp Community Guidelines apply to you when interacting with the community here on GitHub and contributing code.
|
||||
|
||||
Please read the full text at https://www.hashicorp.com/community-guidelines
|
|
@ -0,0 +1,48 @@
|
|||
<!-- Please reserve GitHub issues for bug reports and feature requests.
|
||||
|
||||
For questions, the best place to get answers is on our [mailing list](https://groups.google.com/forum/#!forum/vault-tool), as they will get more visibility from experienced users than the issue tracker.
|
||||
|
||||
Please note: We take Vault's security and our users' trust very seriously. If you believe you have found a security issue in Vault, please responsibly disclose by contacting us at security@hashicorp.com. Our PGP key is available at [our security page](https://www.hashicorp.com/security/).
|
||||
|
||||
-->
|
||||
|
||||
|
||||
<!-- Uncomment this section if this is a feature request. Include or exclude other sections as deemed appropriate.
|
||||
**Feature Request:**
|
||||
-->
|
||||
|
||||
**Environment:**
|
||||
<!-- The version can be retrieved with `vault version`. -->
|
||||
|
||||
* Vault Version:
|
||||
* Operating System/Architecture:
|
||||
|
||||
**Vault Config File:**
|
||||
<!-- Configuration file used for the vault server. -->
|
||||
|
||||
```hcl
|
||||
# Paste your Vault config here.
|
||||
# Be sure to scrub any sensitive values
|
||||
```
|
||||
|
||||
**Startup Log Output:**
|
||||
<!-- Logs from vault's output on startup, if available. -->
|
||||
|
||||
```text
|
||||
# Paste your log output here
|
||||
```
|
||||
|
||||
**Expected Behavior:**
|
||||
<!-- What should have happened? -->
|
||||
|
||||
**Actual Behavior:**
|
||||
<!-- What actually happened? -->
|
||||
|
||||
**Steps to Reproduce:**
|
||||
<!-- List the steps required to reproduce the issue. -->
|
||||
|
||||
**Important Factoids:**
|
||||
<!-- Describe any atypical environment setup, if any. -->
|
||||
|
||||
**References:**
|
||||
<!-- Link to any references, such as GitHub issues or pull requests. -->
|
|
@ -0,0 +1,43 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Let us know about a bug!
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- Please reserve GitHub issues for bug reports and feature requests.
|
||||
|
||||
For questions, the best place to get answers is on our [discussion forum](https://discuss.hashicorp.com/c/vault), as they will get more visibility from experienced users than the issue tracker.
|
||||
|
||||
Please note: We take Vault's security and our users' trust very seriously. If you believe you have found a security issue in Vault, please responsibly disclose by contacting us at security@hashicorp.com. Our PGP key is available at [our security page](https://www.hashicorp.com/security/).
|
||||
|
||||
-->
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Run `vault write ...`
|
||||
2. Run `vault login....`
|
||||
3. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Environment:**
|
||||
* Vault Server Version (retrieve with `vault status`):
|
||||
* Vault CLI Version (retrieve with `vault version`):
|
||||
* Server Operating System/Architecture:
|
||||
|
||||
Vault server configuration file(s):
|
||||
|
||||
```hcl
|
||||
# Paste your Vault config here.
|
||||
# Be sure to scrub any sensitive values
|
||||
```
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
|
@ -0,0 +1,7 @@
|
|||
# Copyright (c) HashiCorp, Inc.
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
contact_links:
|
||||
- name: Ask a question
|
||||
url: https://discuss.hashicorp.com/c/vault
|
||||
about: For increased visibility, please post questions on the discussion forum.
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest something!
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Explain any additional use-cases**
|
||||
If there are any use-cases that would help us understand the use/need/value please share them as they can help us decide on acceptance and prioritization.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
name: Plugin Submission
|
||||
about: Submit a community Vault plugin!
|
||||
title: "[Plugin Portal] Plugin Submission - <PLUGIN NAME>"
|
||||
labels: ecosystem/plugin
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Please provide details for the plugin to be listed. All fields are required for a submission to be included in the [Plugin Portal](https://www.vaultproject.io/docs/plugin-portal) page.
|
||||
|
||||
**Plugin Information**
|
||||
Name as it would appear listed:
|
||||
Plugin type (secrets/auth/database):
|
||||
Repository link:
|
|
@ -0,0 +1,19 @@
|
|||
# Copyright (c) HashiCorp, Inc.
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
self-hosted-runner:
|
||||
# Labels of self-hosted runner in array of string
|
||||
labels:
|
||||
- small
|
||||
- medium
|
||||
- large
|
||||
- ondemand
|
||||
- disk_gb=64
|
||||
- os=linux
|
||||
- type=m5.2xlarge
|
||||
- type=c6a.xlarge
|
||||
- type=c6a.4xlarge
|
||||
- ubuntu-20.04
|
||||
- custom-linux-small-vault-latest
|
||||
- custom-linux-medium-vault-latest
|
||||
- custom-linux-xl-vault-latest
|
|
@ -0,0 +1,71 @@
|
|||
---
|
||||
name: Set up Go with a shared module cache
|
||||
description: Set up Go with a shared module cache
|
||||
|
||||
inputs:
|
||||
github-token:
|
||||
description: "An elevated Github token to access private modules if necessary"
|
||||
type: string
|
||||
no-restore:
|
||||
description: "Whether or not to restore the Go module cache on a cache hit"
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
outputs:
|
||||
cache-key:
|
||||
description: "The Go modules cache key"
|
||||
value: ${{ steps.metadata.outputs.cache-key }}
|
||||
cache-path:
|
||||
description: "The GOMODCACHE path"
|
||||
value: ${{ steps.metadata.outputs.cache-path }}
|
||||
go-version:
|
||||
description: "The version of Go in the .go-version file"
|
||||
value: ${{ steps.go-version.outputs.go-version }}
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- id: go-version
|
||||
shell: bash
|
||||
run: echo "go-version=$(cat ./.go-version)" >> "$GITHUB_OUTPUT"
|
||||
- uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1
|
||||
with:
|
||||
go-version: ${{ steps.go-version.outputs.go-version }}
|
||||
cache: false # We use our own caching strategy
|
||||
- id: metadata
|
||||
shell: bash
|
||||
run: |
|
||||
echo "cache-path=$(go env GOMODCACHE)" >> "$GITHUB_OUTPUT"
|
||||
echo "cache-key=go-modules-${{ hashFiles('**/go.sum') }}" >> "$GITHUB_OUTPUT"
|
||||
- id: cache-modules
|
||||
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
|
||||
with:
|
||||
enableCrossOsArchive: true
|
||||
lookup-only: ${{ inputs.no-restore }}
|
||||
# We need to be very considerate of our caching strategy because Github only allows 10gb
|
||||
# of caches per repository before it starts to evict older caches. This is usually fine
|
||||
# if you only use the actions cache for cache, but we also use it for Go test time results.
|
||||
# These results are used to balance our Go test groups, without which we could have
|
||||
# painfully unbalanced Go test execution times. We have to ensure current caches for all
|
||||
# active release branches and main do not exceed 10gb. Ideally we'd cache Go modules
|
||||
# and Go build cache on a per version/platform/architecture/tag/module basis, but that
|
||||
# would result in several hungred gb over all of our build workflows and release branches.
|
||||
# Instead, we've chosen a middle ground approach where were share Go modules between build
|
||||
# workflows but lose the Go build cache.
|
||||
# We intentionally do not use partial restore keys. If we get dont get an exact cache hit
|
||||
# we only want to download the latest modules, not append them to a prior cache. This
|
||||
# keeps cache upload time, download time, and storage size to a minimum.
|
||||
path: ${{ steps.metadata.outputs.cache-path }}
|
||||
key: ${{ steps.metadata.outputs.cache-key }}
|
||||
- if: steps.cache-modules.outputs.cache-hit != 'true'
|
||||
name: Download go modules
|
||||
shell: bash
|
||||
run: |
|
||||
git config --global url."https://${{ inputs.github-token }}@github.com".insteadOf https://github.com
|
||||
for mod in $(find . -type f -name go.mod); do
|
||||
pushd "$(dirname $mod)"
|
||||
go list ./...
|
||||
go list -test ./...
|
||||
go mod download
|
||||
popd
|
||||
done
|
|
@ -0,0 +1,52 @@
|
|||
---
|
||||
name: Set up gotestsum from Github releases
|
||||
description: Set up gotestsum from Github releases
|
||||
|
||||
inputs:
|
||||
destination:
|
||||
description: "Where to install the gotestsum binary (default: $HOME/bin/gotestsum)"
|
||||
type: boolean
|
||||
default: "$HOME/bin"
|
||||
version:
|
||||
description: "The version to install (default: latest)"
|
||||
type: string
|
||||
default: Latest
|
||||
|
||||
outputs:
|
||||
destination:
|
||||
description: Where the installed gotestsum binary is
|
||||
value: ${{ steps.install.outputs.destination }}
|
||||
destination-dir:
|
||||
description: The directory where the installed gotestsum binary is
|
||||
value: ${{ steps.install.outputs.destination-dir }}
|
||||
version:
|
||||
description: The installed version of gotestsum
|
||||
value: ${{ steps.install.outputs.version }}
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- id: install
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
VERSION=$(gh release list -R gotestyourself/gotestsum --exclude-drafts --exclude-pre-releases | grep Latest | cut -f1)
|
||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||
|
||||
mkdir -p "$HOME/bin"
|
||||
DESTINATION="$(readlink -f "$HOME/bin")"
|
||||
echo "destination=$DESTINATION" >> "GITHUB_OUTPUT"
|
||||
DESTINATION_DIR="$(dirname "$DESTINATION")"
|
||||
echo "$DESTINATION_DIR" >> "$GITHUB_PATH"
|
||||
echo "destination-dir=$DESTINATION_DIR" >> "GITHUB_OUTPUT"
|
||||
|
||||
OS="$(echo "$RUNNER_OS" | tr '[:upper:]' '[:lower:]')"
|
||||
ARCH="$(echo "$RUNNER_ARCH" | tr '[:upper:]' '[:lower:]')"
|
||||
if [ "$ARCH" = "x64" ]; then
|
||||
export ARCH="amd64"
|
||||
fi
|
||||
|
||||
gh release download "$VERSION" -p "*${OS}_${ARCH}.tar.gz" -O gotestsum.tgz -R gotestyourself/gotestsum
|
||||
tar -xvf gotestsum.tgz
|
||||
mv gotestsum "${DESTINATION_DIR}/gotestsum"
|
|
@ -0,0 +1,7 @@
|
|||
version: 2
|
||||
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
|
@ -0,0 +1,38 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
|
||||
function update_or_create_comment {
|
||||
REPO=$1
|
||||
PR_NUMBER=$2
|
||||
SEARCH_KEY=$3
|
||||
BODY=$4
|
||||
|
||||
# We only want for the GH bot to place one comment to report build failures
|
||||
# and if we rerun a job, that comment needs to be updated.
|
||||
# Let's try to find if the GH bot has placed a similar comment
|
||||
comment_id=$(gh api \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
--paginate \
|
||||
/repos/hashicorp/"$REPO"/issues/"$PR_NUMBER"/comments | jq -r --arg SEARCH_KEY "$SEARCH_KEY" '.[] | select (.body | contains($SEARCH_KEY)) | .id')
|
||||
|
||||
if [[ "$comment_id" != "" ]]; then
|
||||
# update the comment with the new body
|
||||
gh api \
|
||||
--method PATCH \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
/repos/hashicorp/"$REPO"/issues/comments/"$comment_id" \
|
||||
-f body="$BODY"
|
||||
else
|
||||
# create a comment with the new body
|
||||
gh api \
|
||||
--method POST \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
/repos/hashicorp/"$REPO"/issues/"$PR_NUMBER"/comments \
|
||||
-f body="$BODY"
|
||||
fi
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# this script expects the following env vars to be set
|
||||
# error if these are not set
|
||||
[ ${GITHUB_TOKEN:?} ]
|
||||
[ ${RUN_ID:?} ]
|
||||
[ ${REPO:?} ]
|
||||
[ ${PR_NUMBER:?} ]
|
||||
# list of build jobs
|
||||
[ ${BUILD_OTHER:?} ]
|
||||
[ ${BUILD_LINUX:?} ]
|
||||
[ ${BUILD_DARWIN:?} ]
|
||||
[ ${BUILD_DOCKER:?} ]
|
||||
[ ${BUILD_UBI:?} ]
|
||||
[ ${TEST:?} ]
|
||||
[ ${TEST_DOCKER_K8S:?} ]
|
||||
|
||||
# listing out all of the jobs with the status
|
||||
jobs=( "build-other:$BUILD_OTHER" "build-linux:$BUILD_LINUX" "build-darwin:$BUILD_DARWIN" "build-docker:$BUILD_DOCKER" "build-ubi:$BUILD_UBI" "test:$TEST" "test-docker-k8s:$TEST_DOCKER_K8S" )
|
||||
|
||||
# there is a case where even if a job is failed, it reports as cancelled. So, we look for both.
|
||||
failed_jobs=()
|
||||
for job in "${jobs[@]}";do
|
||||
if [[ "$job" == *"failure"* || "$job" == *"cancelled"* ]]; then
|
||||
failed_jobs+=("$job")
|
||||
fi
|
||||
done
|
||||
|
||||
# Create a comment to be posted on the PR
|
||||
# This comment reports failed jobs and the url to the failed workflow
|
||||
if [ ${#failed_jobs[@]} -eq 0 ]; then
|
||||
new_body="Build Results:
|
||||
All builds succeeded! :white_check_mark:"
|
||||
else
|
||||
new_body="Build Results:
|
||||
Build failed for these jobs: ${failed_jobs[*]}. Please refer to this workflow to learn more: https://github.com/hashicorp/vault/actions/runs/$RUN_ID"
|
||||
fi
|
||||
|
||||
|
||||
source ./.github/scripts/gh_comment.sh
|
||||
|
||||
update_or_create_comment "$REPO" "$PR_NUMBER" "Build Results:" "$new_body"
|
|
@ -0,0 +1,42 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
MAX_TESTS=10
|
||||
# this script expects the following env vars to be set
|
||||
# error if these are not set
|
||||
[ ${GITHUB_TOKEN:?} ]
|
||||
[ ${RUN_ID:?} ]
|
||||
[ ${REPO:?} ]
|
||||
[ ${PR_NUMBER:?} ]
|
||||
if [ -z "$TABLE_DATA" ]; then
|
||||
BODY="CI Results:
|
||||
All Go tests succeeded! :white_check_mark:"
|
||||
else
|
||||
# Remove any rows that don't have a test name
|
||||
# Only keep the test type, test package, test name, and logs column
|
||||
# Remove the scroll emoji
|
||||
# Remove "github.com/hashicorp/vault" from the package name
|
||||
TABLE_DATA=$(echo "$TABLE_DATA" | awk -F\| '{if ($4 != " - ") { print "|" $2 "|" $3 "|" $4 "|" $7 }}' | sed -r 's/ :scroll://' | sed -r 's/github.com\/hashicorp\/vault\///')
|
||||
NUM_FAILURES=$(wc -l <<< "$TABLE_DATA")
|
||||
|
||||
# Check if the number of failures is greater than the maximum tests to display
|
||||
# If so, limit the table to MAX_TESTS number of results
|
||||
if [ "$NUM_FAILURES" -gt "$MAX_TESTS" ]; then
|
||||
TABLE_DATA=$(echo "$TABLE_DATA" | head -n "$MAX_TESTS")
|
||||
NUM_OTHER=( $NUM_FAILURES - "$MAX_TESTS" )
|
||||
TABLE_DATA="$TABLE_DATA
|
||||
|
||||
and $NUM_OTHER other tests"
|
||||
fi
|
||||
|
||||
# Add the header for the table
|
||||
BODY="CI Results:
|
||||
Failures:
|
||||
| Test Type | Package | Test | Logs |
|
||||
| --------- | ------- | ---- | ---- |
|
||||
${TABLE_DATA}"
|
||||
fi
|
||||
|
||||
source ./.github/scripts/gh_comment.sh
|
||||
|
||||
update_or_create_comment "$REPO" "$PR_NUMBER" "CI Results:" "$BODY"
|
|
@ -0,0 +1,57 @@
|
|||
#!/bin/bash
|
||||
# This script validates if the git diff contains only docs/ui changes
|
||||
|
||||
event_type=$1 # GH event type (pull_request)
|
||||
ref_name=$2 # branch reference that triggered the workflow
|
||||
base_ref=$3 # PR branch base ref
|
||||
|
||||
contains() {
|
||||
target=$1; shift
|
||||
for i; do
|
||||
if [[ "$i" == "$target" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
if [[ "$event_type" == "pull_request" ]]; then
|
||||
git fetch --no-tags --prune origin $base_ref
|
||||
head_commit="HEAD"
|
||||
base_commit="origin/$base_ref"
|
||||
else
|
||||
git fetch --no-tags --prune origin $ref_name
|
||||
head_commit=$(git log origin/$ref_name --oneline | head -1 | awk '{print $1}')
|
||||
base_commit=$(git log origin/$ref_name --oneline | head -2 | awk 'NR==2 {print $1}')
|
||||
fi
|
||||
|
||||
# git diff with ... shows the differences between base_commit and head_commit starting at the last common commit
|
||||
# excluding the changelog directory
|
||||
changed_dir=$(git diff $base_commit...$head_commit --name-only | awk -F"/" '{ print $1}' | uniq | sed '/changelog/d')
|
||||
change_count=$(git diff $base_commit...$head_commit --name-only | awk -F"/" '{ print $1}' | uniq | sed '/changelog/d' | wc -l)
|
||||
|
||||
# There are 4 main conditions to check:
|
||||
#
|
||||
# 1. more than two changes found, set the flags to false
|
||||
# 2. doc only change
|
||||
# 3. ui only change
|
||||
# 4. two changes found, if either doc or ui does not exist in the changes, set both flags to false
|
||||
|
||||
if [[ $change_count -gt 2 ]]; then
|
||||
echo "is_docs_change=false" >> "$GITHUB_OUTPUT"
|
||||
echo "is_ui_change=false" >> "$GITHUB_OUTPUT"
|
||||
elif [[ $change_count -eq 1 && "$changed_dir" == "website" ]]; then
|
||||
echo "is_docs_change=true" >> "$GITHUB_OUTPUT"
|
||||
echo "is_ui_change=false" >> "$GITHUB_OUTPUT"
|
||||
elif [[ $change_count -eq 1 && "$changed_dir" == "ui" ]]; then
|
||||
echo "is_ui_change=true" >> "$GITHUB_OUTPUT"
|
||||
echo "is_docs_change=false" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
if ! contains "website" ${changed_dir[@]} || ! contains "ui" ${changed_dir[@]}; then
|
||||
echo "is_docs_change=false" >> "$GITHUB_OUTPUT"
|
||||
echo "is_ui_change=false" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "is_docs_change=true" >> "$GITHUB_OUTPUT"
|
||||
echo "is_ui_change=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
fi
|
|
@ -0,0 +1,17 @@
|
|||
name: Lint GitHub Actions Workflows
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/**'
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
|
||||
jobs:
|
||||
actionlint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- name: "Check workflow files"
|
||||
uses: docker://docker.mirror.hashicorp.services/rhysd/actionlint@sha256:93834930f56ca380be3e9a3377670d7aa5921be251b9c774891a39b3629b83b8
|
||||
with:
|
||||
# milestoned and demilestoned work (https://github.com/github/docs/issues/23909) but they aren't listed in the github documentation, so actionlint complains about them
|
||||
args: "-ignore=\"invalid activity type \\\"demilestoned\\\" for \\\"pull_request\\\" Webhook event\" -ignore=\"invalid activity type \\\"milestoned\\\" for \\\"pull_request\\\" Webhook event\""
|
|
@ -0,0 +1,26 @@
|
|||
name: Add HashiCorp contributed label
|
||||
|
||||
# The purpose of this job is to label all HashiCorp contributed PRs, so that
|
||||
# we can more easily identify community contributed PRs (anything that doesn't
|
||||
# have this label).
|
||||
# While it might seem like this is the 'reverse' of what we should do, GitHub
|
||||
# (rightly) does not allow branches from forks to have write permissions, so
|
||||
# making PRs from forks self-label themselves as community-contributed is not
|
||||
# possible.
|
||||
|
||||
on:
|
||||
# On every pull request, on every branch
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
add-hashicorp-contributed-label:
|
||||
# Only run if this is NOT coming from a fork of hashicorp/vault (if this is not true, it's community contributed)
|
||||
if: ${{ github.repository == 'hashicorp/vault' && (github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name) }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "Add label to PR"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR: ${{ github.event.pull_request.html_url }}
|
||||
run: gh pr edit "$PR" --add-label 'hashicorp-contributed-pr'
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
name: Backport Assistant Runner (for OSS & ENT)
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- closed
|
||||
- labeled
|
||||
|
||||
jobs:
|
||||
backport-targeted-release-branch:
|
||||
if: github.event.pull_request.merged
|
||||
runs-on: ubuntu-latest
|
||||
container: hashicorpdev/backport-assistant:0.3.3
|
||||
steps:
|
||||
- name: Backport changes to targeted release branch
|
||||
run: |
|
||||
backport-assistant backport -merge-method=squash -gh-automerge
|
||||
env:
|
||||
BACKPORT_LABEL_REGEXP: "backport/(?P<target>\\d+\\.\\d+\\.[+\\w]+)"
|
||||
BACKPORT_TARGET_TEMPLATE: "release/{{.target}}"
|
||||
BACKPORT_MERGE_COMMIT: true
|
||||
GITHUB_TOKEN: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
|
|
@ -0,0 +1,110 @@
|
|||
---
|
||||
name: build_vault
|
||||
|
||||
# This workflow is intended to be called by the build workflow for each Vault
|
||||
# binary that needs to be built and packaged. The ci make targets that are
|
||||
# utilized automatically determine build metadata and handle building and
|
||||
# packing vault.
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
cgo-enabled:
|
||||
type: string
|
||||
default: 0
|
||||
create-packages:
|
||||
type: boolean
|
||||
default: true
|
||||
goos:
|
||||
required: true
|
||||
type: string
|
||||
goarch:
|
||||
required: true
|
||||
type: string
|
||||
go-tags:
|
||||
type: string
|
||||
package-name:
|
||||
type: string
|
||||
default: vault
|
||||
vault-version:
|
||||
type: string
|
||||
required: true
|
||||
web-ui-cache-key:
|
||||
type: string
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: custom-linux-xl-vault-latest
|
||||
name: Vault ${{ inputs.goos }} ${{ inputs.goarch }} v${{ inputs.vault-version }}
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: ./.github/actions/set-up-go
|
||||
with:
|
||||
github-token: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
|
||||
- name: Restore UI from cache
|
||||
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
|
||||
with:
|
||||
# Restore the UI asset from the UI build workflow. Never use a partial restore key.
|
||||
enableCrossOsArchive: true
|
||||
fail-on-cache-miss: true
|
||||
path: http/web_ui
|
||||
key: ${{ inputs.web-ui-cache-key }}
|
||||
- name: Build Vault
|
||||
env:
|
||||
GO_TAGS: ${{ inputs.go-tags }}
|
||||
CGO_ENABLED: ${{ inputs.cgo-enabled }}
|
||||
GOARCH: ${{ inputs.goarch }}
|
||||
GOOS: ${{ inputs.goos }}
|
||||
VERSION: ${{ inputs.vault-version }}
|
||||
run:
|
||||
make ci-build
|
||||
- name: Determine artifact basename
|
||||
env:
|
||||
GOARCH: ${{ inputs.goarch }}
|
||||
GOOS: ${{ inputs.goos }}
|
||||
VERSION: ${{ inputs.vault-version }}
|
||||
run: echo "ARTIFACT_BASENAME=$(make ci-get-artifact-basename)" >> "$GITHUB_ENV"
|
||||
- name: Bundle Vault
|
||||
env:
|
||||
BUNDLE_PATH: out/${{ env.ARTIFACT_BASENAME }}.zip
|
||||
run: make ci-bundle
|
||||
- uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
with:
|
||||
name: ${{ env.ARTIFACT_BASENAME }}.zip
|
||||
path: out/${{ env.ARTIFACT_BASENAME }}.zip
|
||||
if-no-files-found: error
|
||||
- if: ${{ inputs.create-packages }}
|
||||
uses: hashicorp/actions-packaging-linux@v1
|
||||
with:
|
||||
name: ${{ github.event.repository.name }}
|
||||
description: Vault is a tool for secrets management, encryption as a service, and privileged access management.
|
||||
arch: ${{ inputs.goarch }}
|
||||
version: ${{ inputs.vault-version }}
|
||||
maintainer: HashiCorp
|
||||
homepage: https://github.com/hashicorp/vault
|
||||
license: MPL-2.0
|
||||
binary: dist/${{ inputs.package-name }}
|
||||
deb_depends: openssl
|
||||
rpm_depends: openssl
|
||||
config_dir: .release/linux/package/
|
||||
preinstall: .release/linux/preinst
|
||||
postinstall: .release/linux/postinst
|
||||
postremove: .release/linux/postrm
|
||||
- if: ${{ inputs.create-packages }}
|
||||
name: Determine package file names
|
||||
run: |
|
||||
echo "RPM_PACKAGE=$(basename out/*.rpm)" >> "$GITHUB_ENV"
|
||||
echo "DEB_PACKAGE=$(basename out/*.deb)" >> "$GITHUB_ENV"
|
||||
- if: ${{ inputs.create-packages }}
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
with:
|
||||
name: ${{ env.RPM_PACKAGE }}
|
||||
path: out/${{ env.RPM_PACKAGE }}
|
||||
if-no-files-found: error
|
||||
- if: ${{ inputs.create-packages }}
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
with:
|
||||
name: ${{ env.DEB_PACKAGE }}
|
||||
path: out/${{ env.DEB_PACKAGE }}
|
||||
if-no-files-found: error
|
|
@ -0,0 +1,456 @@
|
|||
name: build
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
# The default types for pull_request are [ opened, synchronize, reopened ].
|
||||
# This is insufficient for our needs, since we're skipping stuff on PRs in
|
||||
# draft mode. By adding the ready_for_review type, when a draft pr is marked
|
||||
# ready, we run everything, including the stuff we'd have skipped up until now.
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release/**
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.head_ref || github.run_id }}-build
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# verify-changes determines if the changes are only for docs (website)
|
||||
verify-changes:
|
||||
uses: ./.github/workflows/verify_changes.yml
|
||||
|
||||
product-metadata:
|
||||
# do not run build and test steps for docs changes
|
||||
# Following https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/troubleshooting-required-status-checks#handling-skipped-but-required-checks
|
||||
# we conditionally skip the build and tests for docs(website) changes
|
||||
if: |
|
||||
github.event.pull_request.draft == false &&
|
||||
needs.verify-changes.outputs.is_docs_change == 'false'
|
||||
runs-on: ubuntu-latest
|
||||
needs: verify-changes
|
||||
outputs:
|
||||
build-date: ${{ steps.get-metadata.outputs.build-date }}
|
||||
filepath: ${{ steps.generate-metadata-file.outputs.filepath }}
|
||||
package-name: ${{ steps.get-metadata.outputs.package-name }}
|
||||
vault-revision: ${{ steps.get-metadata.outputs.vault-revision }}
|
||||
vault-version: ${{ steps.set-product-version.outputs.product-version }}
|
||||
vault-version-package: ${{ steps.get-metadata.outputs.vault-version-package }}
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- name: Ensure Go modules are cached
|
||||
uses: ./.github/actions/set-up-go
|
||||
id: set-up-go
|
||||
with:
|
||||
github-token: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
|
||||
no-restore: true # don't download them on a cache hit
|
||||
- name: Set Product version
|
||||
id: set-product-version
|
||||
uses: hashicorp/actions-set-product-version@v1
|
||||
- name: Get metadata
|
||||
id: get-metadata
|
||||
env:
|
||||
VAULT_VERSION: ${{ steps.set-product-version.outputs.product-version }}
|
||||
run: |
|
||||
# shellcheck disable=SC2129
|
||||
echo "build-date=$(make ci-get-date)" >> "$GITHUB_OUTPUT"
|
||||
echo "package-name=vault" >> "$GITHUB_OUTPUT"
|
||||
echo "vault-revision=$(make ci-get-revision)" >> "$GITHUB_OUTPUT"
|
||||
echo "vault-version-package=$(make ci-get-version-package)" >> "$GITHUB_OUTPUT"
|
||||
- uses: hashicorp/actions-generate-metadata@v1
|
||||
id: generate-metadata-file
|
||||
with:
|
||||
version: ${{ steps.set-product-version.outputs.product-version }}
|
||||
product: ${{ steps.get-metadata.outputs.package-name }}
|
||||
- uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
with:
|
||||
name: metadata.json
|
||||
path: ${{ steps.generate-metadata-file.outputs.filepath }}
|
||||
if-no-files-found: error
|
||||
|
||||
build-ui:
|
||||
name: UI
|
||||
runs-on: custom-linux-xl-vault-latest
|
||||
outputs:
|
||||
cache-key: ui-${{ steps.ui-hash.outputs.ui-hash }}
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- name: Get UI hash
|
||||
id: ui-hash
|
||||
run: echo "ui-hash=$(git ls-tree HEAD ui --object-only)" >> "$GITHUB_OUTPUT"
|
||||
- name: Set up UI asset cache
|
||||
id: cache-ui-assets
|
||||
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
|
||||
with:
|
||||
enableCrossOsArchive: true
|
||||
lookup-only: true
|
||||
path: http/web_ui
|
||||
# Only restore the UI asset cache if we haven't modified anything in the ui directory.
|
||||
# Never do a partial restore of the web_ui if we don't get a cache hit.
|
||||
key: ui-${{ steps.ui-hash.outputs.ui-hash }}
|
||||
- if: steps.cache-ui-assets.outputs.cache-hit != 'true'
|
||||
name: Set up node and yarn
|
||||
uses: actions/setup-node@e33196f7422957bea03ed53f6fbb155025ffc7b8 # v3.7.0
|
||||
with:
|
||||
node-version-file: ui/package.json
|
||||
cache: yarn
|
||||
cache-dependency-path: ui/yarn.lock
|
||||
- if: steps.cache-ui-assets.outputs.cache-hit != 'true'
|
||||
name: Build UI
|
||||
run: make ci-build-ui
|
||||
|
||||
build-other:
|
||||
name: Other
|
||||
needs:
|
||||
- product-metadata
|
||||
- build-ui
|
||||
strategy:
|
||||
matrix:
|
||||
goos: [freebsd, windows, netbsd, openbsd, solaris]
|
||||
goarch: [386, amd64, arm]
|
||||
exclude:
|
||||
- goos: solaris
|
||||
goarch: 386
|
||||
- goos: solaris
|
||||
goarch: arm
|
||||
- goos: windows
|
||||
goarch: arm
|
||||
fail-fast: true
|
||||
uses: ./.github/workflows/build-vault-ce.yml
|
||||
with:
|
||||
create-packages: false
|
||||
goarch: ${{ matrix.goarch }}
|
||||
goos: ${{ matrix.goos }}
|
||||
go-tags: ui
|
||||
package-name: ${{ needs.product-metadata.outputs.package-name }}
|
||||
web-ui-cache-key: ${{ needs.build-ui.outputs.cache-key }}
|
||||
vault-version: ${{ needs.product-metadata.outputs.vault-version }}
|
||||
secrets: inherit
|
||||
|
||||
build-linux:
|
||||
name: Linux
|
||||
needs:
|
||||
- product-metadata
|
||||
- build-ui
|
||||
strategy:
|
||||
matrix:
|
||||
goos: [linux]
|
||||
goarch: [arm, arm64, 386, amd64]
|
||||
fail-fast: true
|
||||
uses: ./.github/workflows/build-vault-ce.yml
|
||||
with:
|
||||
goarch: ${{ matrix.goarch }}
|
||||
goos: ${{ matrix.goos }}
|
||||
go-tags: ui
|
||||
package-name: ${{ needs.product-metadata.outputs.package-name }}
|
||||
web-ui-cache-key: ${{ needs.build-ui.outputs.cache-key }}
|
||||
vault-version: ${{ needs.product-metadata.outputs.vault-version }}
|
||||
secrets: inherit
|
||||
|
||||
build-darwin:
|
||||
name: Darwin
|
||||
needs:
|
||||
- product-metadata
|
||||
- build-ui
|
||||
strategy:
|
||||
matrix:
|
||||
goos: [darwin]
|
||||
goarch: [amd64, arm64]
|
||||
fail-fast: true
|
||||
uses: ./.github/workflows/build-vault-ce.yml
|
||||
with:
|
||||
create-packages: false
|
||||
goarch: ${{ matrix.goarch }}
|
||||
goos: ${{ matrix.goos }}
|
||||
go-tags: ui
|
||||
package-name: ${{ needs.product-metadata.outputs.package-name }}
|
||||
web-ui-cache-key: ${{ needs.build-ui.outputs.cache-key }}
|
||||
vault-version: ${{ needs.product-metadata.outputs.vault-version }}
|
||||
secrets: inherit
|
||||
|
||||
build-docker:
|
||||
name: Docker image
|
||||
needs:
|
||||
- product-metadata
|
||||
- build-linux
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [arm, arm64, 386, amd64]
|
||||
env:
|
||||
repo: ${{ github.event.repository.name }}
|
||||
version: ${{ needs.product-metadata.outputs.vault-version }}
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: hashicorp/actions-docker-build@v1
|
||||
with:
|
||||
version: ${{ env.version }}
|
||||
target: default
|
||||
arch: ${{ matrix.arch }}
|
||||
zip_artifact_name: vault_${{ env.version }}_linux_${{ matrix.arch }}.zip
|
||||
tags: |
|
||||
docker.io/hashicorp/${{ env.repo }}:${{ env.version }}
|
||||
public.ecr.aws/hashicorp/${{ env.repo }}:${{ env.version }}
|
||||
|
||||
build-ubi:
|
||||
name: UBI image
|
||||
needs:
|
||||
- product-metadata
|
||||
- build-linux
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [amd64]
|
||||
env:
|
||||
repo: ${{ github.event.repository.name }}
|
||||
version: ${{ needs.product-metadata.outputs.vault-version }}
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: hashicorp/actions-docker-build@v1
|
||||
with:
|
||||
version: ${{ env.version }}
|
||||
target: ubi
|
||||
arch: ${{ matrix.arch }}
|
||||
zip_artifact_name: vault_${{ env.version }}_linux_${{ matrix.arch }}.zip
|
||||
# The redhat_tag differs on CE and ENT editions. Be mindful when resolving merge conflicts.
|
||||
redhat_tag: quay.io/redhat-isv-containers/5f89bb5e0b94cf64cfeb500a:${{ env.version }}-ubi
|
||||
|
||||
test:
|
||||
name: Test ${{ matrix.build-artifact-name }}
|
||||
# Only run the Enos workflow against branches that are created from the
|
||||
# hashicorp/vault repository. This has the effect of limiting execution of
|
||||
# Enos scenarios to branches that originate from authors that have write
|
||||
# access to hashicorp/vault repository. This is required as Github Actions
|
||||
# will not populate the required secrets for branches created by outside
|
||||
# contributors in order to protect the secrets integrity.
|
||||
# This condition can be removed in future if enos workflow is updated to
|
||||
# workflow_run event
|
||||
if: "! github.event.pull_request.head.repo.fork"
|
||||
needs:
|
||||
- product-metadata
|
||||
- build-linux
|
||||
uses: ./.github/workflows/test-run-enos-scenario-matrix.yml
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- sample-name: build_ce_linux_amd64_deb
|
||||
build-artifact-name: vault_${{ needs.product-metadata.outputs.vault-version-package }}-1_amd64.deb
|
||||
- sample-name: build_ce_linux_arm64_deb
|
||||
build-artifact-name: vault_${{ needs.product-metadata.outputs.vault-version-package }}-1_arm64.deb
|
||||
- sample-name: build_ce_linux_amd64_rpm
|
||||
build-artifact-name: vault-${{ needs.product-metadata.outputs.vault-version-package }}-1.x86_64.rpm
|
||||
- sample-name: build_ce_linux_arm64_rpm
|
||||
build-artifact-name: vault-${{ needs.product-metadata.outputs.vault-version-package }}-1.aarch64.rpm
|
||||
- sample-name: build_ce_linux_amd64_zip
|
||||
build-artifact-name: vault_${{ needs.product-metadata.outputs.vault-version }}_linux_amd64.zip
|
||||
- sample-name: build_ce_linux_arm64_zip
|
||||
build-artifact-name: vault_${{ needs.product-metadata.outputs.vault-version }}_linux_arm64.zip
|
||||
with:
|
||||
build-artifact-name: ${{ matrix.build-artifact-name }}
|
||||
sample-max: 1
|
||||
sample-name: ${{ matrix.sample-name }}
|
||||
ssh-key-name: ${{ github.event.repository.name }}-ci-ssh-key
|
||||
vault-revision: ${{ needs.product-metadata.outputs.vault-revision }}
|
||||
vault-version: ${{ needs.product-metadata.outputs.vault-version }}
|
||||
secrets: inherit
|
||||
|
||||
test-docker-k8s:
|
||||
name: Test Docker K8s
|
||||
# Only run the Enos workflow against branches that are created from the
|
||||
# hashicorp/vault repository. This has the effect of limiting execution of
|
||||
# Enos scenarios to branches that originate from authors that have write
|
||||
# access to hashicorp/vault repository. This is required as Github Actions
|
||||
# will not populate the required secrets for branches created by outside
|
||||
# contributors in order to protect the secrets integrity.
|
||||
# GHA secrets are only ready on workflow_run for public repo
|
||||
# This condition can be removed in future if enos workflow is updated to
|
||||
# workflow_run event
|
||||
if: "! github.event.pull_request.head.repo.fork"
|
||||
needs:
|
||||
- product-metadata
|
||||
- build-docker
|
||||
uses: ./.github/workflows/enos-run-k8s.yml
|
||||
with:
|
||||
artifact-build-date: ${{ needs.product-metadata.outputs.build-date }}
|
||||
artifact-name: ${{ github.event.repository.name }}_default_linux_amd64_${{ needs.product-metadata.outputs.vault-version }}_${{ needs.product-metadata.outputs.vault-revision }}.docker.tar
|
||||
artifact-revision: ${{ needs.product-metadata.outputs.vault-revision }}
|
||||
artifact-version: ${{ needs.product-metadata.outputs.vault-version }}
|
||||
secrets: inherit
|
||||
|
||||
report-build-failures:
|
||||
name: Report Build Failures
|
||||
needs:
|
||||
- build-other
|
||||
- build-linux
|
||||
- build-darwin
|
||||
- build-docker
|
||||
- build-ubi
|
||||
- test
|
||||
- test-docker-k8s
|
||||
if: (success() || failure()) && github.head_ref != ''
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- name: Build Status
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
RUN_ID: ${{ github.run_id }}
|
||||
REPO: ${{ github.event.repository.name }}
|
||||
BUILD_OTHER: ${{ needs.build-other.result }}
|
||||
BUILD_LINUX: ${{ needs.build-linux.result }}
|
||||
BUILD_DARWIN: ${{ needs.build-darwin.result }}
|
||||
BUILD_DOCKER: ${{ needs.build-docker.result }}
|
||||
BUILD_UBI: ${{ needs.build-ubi.result }}
|
||||
TEST: ${{ needs.test.result }}
|
||||
TEST_DOCKER_K8S: ${{ needs.test-docker-k8s.result }}
|
||||
run: ./.github/scripts/report_failed_builds.sh
|
||||
|
||||
completed-successfully:
|
||||
# We force a failure if any of the dependent jobs fail,
|
||||
# this is a workaround for the issue reported https://github.com/actions/runner/issues/2566
|
||||
if: always()
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build-other
|
||||
- build-linux
|
||||
- build-darwin
|
||||
- build-docker
|
||||
- build-ubi
|
||||
- test
|
||||
- test-docker-k8s
|
||||
steps:
|
||||
- run: |
|
||||
tr -d '\n' <<< '${{ toJSON(needs.*.result) }}' | grep -q -v -E '(failure|cancelled)'
|
||||
|
||||
notify-completed-successfully-failures-ce:
|
||||
if: ${{ always() && github.repository == 'hashicorp/vault' && needs.completed-successfully.result == 'failure' && (github.ref_name == 'main' || startsWith(github.ref_name, 'release/')) }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
needs:
|
||||
- completed-successfully
|
||||
- build-other
|
||||
- build-linux
|
||||
- build-darwin
|
||||
- build-docker
|
||||
- build-ubi
|
||||
- test
|
||||
- test-docker-k8s
|
||||
steps:
|
||||
- name: send-notification
|
||||
uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0
|
||||
# We intentionally aren't using the following here since it's from an internal repo
|
||||
# uses: hashicorp/cloud-gha-slack-notifier@730a033037b8e603adf99ebd3085f0fdfe75e2f4 #v1
|
||||
env:
|
||||
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
|
||||
with:
|
||||
channel-id: "C05AABYEA9Y" # sent to #feed-vault-ci-official, use "C05Q4D5V89W"/test-vault-ci-slack-integration for testing
|
||||
payload: |
|
||||
{
|
||||
"text": "CE build failures on ${{ github.ref_name }}",
|
||||
"blocks": [
|
||||
{
|
||||
"type": "header",
|
||||
"text": {
|
||||
"type": "plain_text",
|
||||
"text": ":rotating_light: CE build failures on ${{ github.ref_name }} :rotating_light:",
|
||||
"emoji": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "divider"
|
||||
},
|
||||
{
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": "${{ (needs.build-other.result != 'failure' && needs.build-linux.result != 'failure' && needs.build-darwin.result != 'failure' && needs.build-docker.result != 'failure' && needs.build-ubi.result != 'failure') && ':white_check_mark:' || ':x:' }} Build results\n${{ (needs.test.result != 'failure' && needs.test-docker-k8s.result != 'failure') && ':white_check_mark:' || ':x:' }} Enos tests"
|
||||
},
|
||||
"accessory": {
|
||||
"type": "button",
|
||||
"text": {
|
||||
"type": "plain_text",
|
||||
"text": "View Failing Workflow",
|
||||
"emoji": true
|
||||
},
|
||||
"url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
notify-completed-successfully-failures-ent:
|
||||
if: ${{ always() && github.repository == 'hashicorp/vault-enterprise' && needs.completed-successfully.result == 'failure' && (github.ref_name == 'main' || startsWith(github.ref_name, 'release/')) }}
|
||||
runs-on: ['self-hosted', 'linux', 'small']
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
needs:
|
||||
- completed-successfully
|
||||
- build-other
|
||||
- build-linux
|
||||
- build-darwin
|
||||
- build-docker
|
||||
- build-ubi
|
||||
- test
|
||||
- test-docker-k8s
|
||||
steps:
|
||||
- id: vault-auth
|
||||
name: Vault Authenticate
|
||||
run: vault-auth
|
||||
- id: secrets
|
||||
name: Fetch Vault Secrets
|
||||
uses: hashicorp/vault-action@130d1f5f4fe645bb6c83e4225c04d64cfb62de6e
|
||||
with:
|
||||
url: ${{ steps.vault-auth.outputs.addr }}
|
||||
caCertificate: ${{ steps.vault-auth.outputs.ca_certificate }}
|
||||
token: ${{ steps.vault-auth.outputs.token }}
|
||||
secrets: |
|
||||
kv/data/github/${{ github.repository }}/github_actions_notifications_bot token | SLACK_BOT_TOKEN;
|
||||
- name: send-notification
|
||||
uses: hashicorp/cloud-gha-slack-notifier@730a033037b8e603adf99ebd3085f0fdfe75e2f4 #v1
|
||||
with:
|
||||
channel-id: "C05AABYEA9Y" # sent to #feed-vault-ci-official, use "C05Q4D5V89W"/test-vault-ci-slack-integration for testing
|
||||
slack-bot-token: ${{ steps.secrets.outputs.SLACK_BOT_TOKEN }}
|
||||
payload: |
|
||||
{
|
||||
"text": "Enterprise build failures on ${{ github.ref_name }}",
|
||||
"blocks": [
|
||||
{
|
||||
"type": "header",
|
||||
"text": {
|
||||
"type": "plain_text",
|
||||
"text": ":rotating_light: Enterprise build failures on ${{ github.ref_name }} :rotating_light:",
|
||||
"emoji": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "divider"
|
||||
},
|
||||
{
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": "${{ (needs.build-other.result != 'failure' && needs.build-linux.result != 'failure' && needs.build-darwin.result != 'failure' && needs.build-docker.result != 'failure' && needs.build-ubi.result != 'failure') && ':white_check_mark:' || ':x:' }} Build results\n${{ (needs.test.result != 'failure' && needs.test-docker-k8s.result != 'failure') && ':white_check_mark:' || ':x:' }} Enos tests"
|
||||
},
|
||||
"accessory": {
|
||||
"type": "button",
|
||||
"text": {
|
||||
"type": "plain_text",
|
||||
"text": "View Failing Workflow",
|
||||
"emoji": true
|
||||
},
|
||||
"url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
# This workflow checks that there is either a 'pr/no-changelog' label applied to a PR
|
||||
# or there is a changelog/<pr number>.txt file associated with a PR for a changelog entry
|
||||
|
||||
name: Check Changelog
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, labeled, unlabeled]
|
||||
# Runs on PRs to main
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
# checks that a changelog entry is present for a PR
|
||||
changelog-check:
|
||||
# If there a `pr/no-changelog` label we ignore this check
|
||||
if: "!contains(github.event.pull_request.labels.*.name, 'pr/no-changelog')"
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
fetch-depth: 0 # by default the checkout action doesn't checkout all branches
|
||||
- name: Check for changelog entry in diff
|
||||
run: |
|
||||
# Check if there is a diff in the changelog directory.
|
||||
#
|
||||
# Try to identify the expected changelog file name based on PR
|
||||
# number. This won't work for Go version updates though.
|
||||
if [ ${{ github.event.repository.name }} == "vault-enterprise" ]; then
|
||||
expected_changelog_file=changelog/_${{ github.event.pull_request.number }}.txt
|
||||
else
|
||||
expected_changelog_file=changelog/${{ github.event.pull_request.number }}.txt
|
||||
fi
|
||||
|
||||
echo "looking for changelog file ${expected_changelog_file}"
|
||||
changelog_files=$(git --no-pager diff --name-only HEAD "$(git merge-base HEAD "origin/${{ github.event.pull_request.base.ref }}")" -- ${expected_changelog_file})
|
||||
|
||||
if [ -z "$changelog_files" ]; then
|
||||
echo "Not found."
|
||||
echo "looking for changelog file matching changelog/_go-ver-*.txt"
|
||||
# If we do not find a file matching the PR # in changelog/, we fail the check
|
||||
# unless we did a Go toolchain version update, in which case we check the
|
||||
# alternative name.
|
||||
toolchain_files=$(git --no-pager diff --name-only HEAD "$(git merge-base HEAD "origin/${{ github.event.pull_request.base.ref }}")" -- 'changelog/_go-ver-*.txt')
|
||||
if [ -z "$toolchain_files" ]; then
|
||||
echo "Not found."
|
||||
echo ""
|
||||
echo "Did not find a changelog entry named ${expected_changelog_file}"
|
||||
echo "If your changelog file is correct, skip this check with the 'pr/no-changelog' label"
|
||||
echo "Reference - https://github.com/hashicorp/vault/pull/10363 and https://github.com/hashicorp/vault/pull/11894"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Else, we found some toolchain files. Let's make sure the contents are correct.
|
||||
if ! grep -q 'release-note:change' "$toolchain_files" || ! grep -q '^core: Bump Go version to' "$toolchain_files"; then
|
||||
echo "Invalid format for changelog. Expected format:"
|
||||
echo '```release-note:change'
|
||||
echo "core: Bump Go version to x.y.z."
|
||||
echo '```'
|
||||
exit 1
|
||||
else
|
||||
echo "Found Go toolchain changelog entry in PR!"
|
||||
fi
|
||||
elif grep -q ':enhancement$' "$changelog_files"; then
|
||||
# "Enhancement is not a valid type of changelog entry, but it's a common mistake.
|
||||
echo "Found invalid type (enhancement) in changelog - did you mean improvement?"
|
||||
exit 1
|
||||
elif grep -q ':changes$' "$changelog_files"; then
|
||||
echo "Found invalid type (changes) in changelog - did you mean change?"
|
||||
exit 1
|
||||
elif grep -q ':bugs$' "$changelog_files"; then
|
||||
echo "Found invalid type (bugs) in changelog - did you mean bug?"
|
||||
exit 1
|
||||
elif grep -q ':fix$' "$changelog_files"; then
|
||||
echo "Found invalid type (fix) in changelog - did you mean bug?"
|
||||
exit 1
|
||||
elif ! grep -q '```release-note:' "$changelog_files"; then
|
||||
# People often make changelog files like ```changelog:, which is incorrect.
|
||||
echo "Changelog file did not contain 'release-note' heading - check formatting."
|
||||
exit 1
|
||||
elif grep -q '^core: Bump Go version' "$changelog_files"; then
|
||||
echo "Don't use PR numbered changelog entries for Go version bumps!"
|
||||
echo "Please use the format changelog/_go-ver-<VAULT_VERSION_WITHOUT_DOTS>.txt instead."
|
||||
echo "Example: _go-ver-1110.txt for Vault 1.11.0"
|
||||
exit 1
|
||||
else
|
||||
echo "Found changelog entry in PR!"
|
||||
fi
|
|
@ -0,0 +1,435 @@
|
|||
name: CI
|
||||
on:
|
||||
pull_request:
|
||||
# The default types for pull_request are [ opened, synchronize, reopened ].
|
||||
# This is insufficient for our needs, since we're skipping stuff on PRs in
|
||||
# draft mode. By adding the ready_for_review type, when a draft pr is marked
|
||||
# ready, we run everything, including the stuff we'd have skipped up until now.
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release/**
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.head_ref || github.run_id }}-ci
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
name: Setup
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
compute-small: ${{ steps.setup-outputs.outputs.compute-small }}
|
||||
compute-medium: ${{ steps.setup-outputs.outputs.compute-medium }}
|
||||
compute-large: ${{ steps.setup-outputs.outputs.compute-large }}
|
||||
compute-largem: ${{ steps.setup-outputs.outputs.compute-largem }}
|
||||
compute-xlarge: ${{ steps.setup-outputs.outputs.compute-xlarge }}
|
||||
enterprise: ${{ steps.setup-outputs.outputs.enterprise }}
|
||||
go-tags: ${{ steps.setup-outputs.outputs.go-tags }}
|
||||
checkout-ref: ${{ steps.checkout-ref-output.outputs.checkout-ref }}
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- id: setup-outputs
|
||||
name: Setup outputs
|
||||
run: |
|
||||
github_repository="${{ github.repository }}"
|
||||
|
||||
if [ "${github_repository##*/}" == "vault-enterprise" ] ; then
|
||||
# shellcheck disable=SC2129
|
||||
echo 'compute-small=["self-hosted","ondemand","linux","type=c6a.large"]' >> "$GITHUB_OUTPUT" # 2x vCPUs, 4 GiB RAM,
|
||||
echo 'compute-medium=["self-hosted","ondemand","linux","type=c6a.xlarge"]' >> "$GITHUB_OUTPUT" # 4x vCPUs, 8 GiB RAM,
|
||||
echo 'compute-large=["self-hosted","ondemand","linux","type=c6a.2xlarge","disk_gb=64"]' >> "$GITHUB_OUTPUT" # 8x vCPUs, 16 GiB RAM,
|
||||
echo 'compute-largem=["self-hosted","ondemand","linux","type=m6a.2xlarge"]' >> "$GITHUB_OUTPUT" # 8x vCPUs, 32 GiB RAM,
|
||||
echo 'compute-xlarge=["self-hosted","ondemand","linux","type=c6a.4xlarge"]' >> "$GITHUB_OUTPUT" # 16x vCPUs, 32 GiB RAM,
|
||||
echo 'enterprise=1' >> "$GITHUB_OUTPUT"
|
||||
echo 'go-tags=ent,enterprise' >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
# shellcheck disable=SC2129
|
||||
echo 'compute-small="ubuntu-latest"' >> "$GITHUB_OUTPUT" # 2x vCPUs, 7 GiB RAM, 14 GB SSD
|
||||
echo 'compute-medium="custom-linux-small-vault-latest"' >> "$GITHUB_OUTPUT" # 8x vCPUs, 32 GiB RAM, 300 GB SSD
|
||||
echo 'compute-large="custom-linux-medium-vault-latest"' >> "$GITHUB_OUTPUT" # 16x vCPUs, 64 GiB RAM, 600 GB SSD
|
||||
echo 'compute-largem="custom-linux-medium-vault-latest"' >> "$GITHUB_OUTPUT" # 16x vCPUs, 64 GiB RAM, 600 GB SSD
|
||||
echo 'compute-xlarge="custom-linux-xl-vault-latest"' >> "$GITHUB_OUTPUT" # 32x vCPUs, 128 GiB RAM, 1200 GB SSD
|
||||
echo 'enterprise=' >> "$GITHUB_OUTPUT"
|
||||
echo 'go-tags=' >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
- name: Ensure Go modules are cached
|
||||
uses: ./.github/actions/set-up-go
|
||||
with:
|
||||
github-token: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
|
||||
no-restore: true # don't download them on a cache hit
|
||||
# control checking out head instead of default ref by a GH label
|
||||
# if checkout-head label is added to a PR, checkout HEAD otherwise checkout ref
|
||||
- if: ${{ !contains(github.event.pull_request.labels.*.name, 'checkout-head') }}
|
||||
run: echo "CHECKOUT_REF=${{ github.ref }}" >> "$GITHUB_ENV"
|
||||
- if: ${{ contains(github.event.pull_request.labels.*.name, 'checkout-head') }}
|
||||
run: echo "CHECKOUT_REF=${{ github.event.pull_request.head.sha }}" >> "$GITHUB_ENV"
|
||||
- id: checkout-ref-output
|
||||
run: echo "checkout-ref=${{ env.CHECKOUT_REF }}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
diff-oss-ci:
|
||||
name: Diff OSS
|
||||
needs:
|
||||
- setup
|
||||
if: ${{ needs.setup.outputs.enterprise != '' && github.base_ref != '' }}
|
||||
runs-on: ${{ fromJSON(needs.setup.outputs.compute-small) }}
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- id: determine-branch
|
||||
run: |
|
||||
branch="${{ github.base_ref }}"
|
||||
|
||||
if [[ $branch = release/* ]] ; then
|
||||
branch=${branch%%+ent}
|
||||
|
||||
# Add OSS remote
|
||||
git config --global user.email "github-team-secret-vault-core@hashicorp.com"
|
||||
git config --global user.name "hc-github-team-secret-vault-core"
|
||||
git remote add oss https://github.com/hashicorp/vault.git
|
||||
git fetch oss "$branch"
|
||||
|
||||
branch="oss/$branch"
|
||||
else
|
||||
branch="origin/$branch"
|
||||
fi
|
||||
|
||||
echo "BRANCH=$branch" >> "$GITHUB_OUTPUT"
|
||||
- id: diff
|
||||
run: |
|
||||
./.github/scripts/oss-diff.sh ${{ steps.determine-branch.outputs.BRANCH }} HEAD
|
||||
|
||||
verify-changes:
|
||||
name: Verify doc-ui only PRs
|
||||
uses: ./.github/workflows/verify_changes.yml
|
||||
|
||||
test-go:
|
||||
name: Run Go tests
|
||||
needs:
|
||||
- setup
|
||||
- verify-changes
|
||||
# Don't run this job for docs/ui only PRs
|
||||
if: |
|
||||
needs.verify-changes.outputs.is_docs_change == 'false' &&
|
||||
needs.verify-changes.outputs.is_ui_change == 'false'
|
||||
uses: ./.github/workflows/test-go.yml
|
||||
with:
|
||||
# The regular Go tests use an extra runner to execute the
|
||||
# binary-dependent tests. We isolate them there so that the
|
||||
# other tests aren't slowed down waiting for a binary build.
|
||||
binary-tests: true
|
||||
total-runners: 16
|
||||
go-arch: amd64
|
||||
go-tags: '${{ needs.setup.outputs.go-tags }},deadlock'
|
||||
runs-on: ${{ needs.setup.outputs.compute-large }}
|
||||
enterprise: ${{ needs.setup.outputs.enterprise }}
|
||||
checkout-ref: ${{ needs.setup.outputs.checkout-ref }}
|
||||
secrets: inherit
|
||||
|
||||
test-go-testonly:
|
||||
name: Run Go tests tagged with testonly
|
||||
needs:
|
||||
- setup
|
||||
- verify-changes
|
||||
# Don't run this job for docs/ui only PRs
|
||||
if: |
|
||||
needs.verify-changes.outputs.is_docs_change == 'false' &&
|
||||
needs.verify-changes.outputs.is_ui_change == 'false'
|
||||
uses: ./.github/workflows/test-go.yml
|
||||
with:
|
||||
testonly: true
|
||||
total-runners: 2 # test runners cannot be less than 2
|
||||
go-arch: amd64
|
||||
go-tags: '${{ needs.setup.outputs.go-tags }},deadlock,testonly'
|
||||
runs-on: ${{ needs.setup.outputs.compute-large }}
|
||||
enterprise: ${{ needs.setup.outputs.enterprise }}
|
||||
secrets: inherit
|
||||
|
||||
test-go-race:
|
||||
name: Run Go tests with data race detection
|
||||
needs:
|
||||
- setup
|
||||
- verify-changes
|
||||
# Don't run this job for docs/ui only PRs
|
||||
if: |
|
||||
github.event.pull_request.draft == false &&
|
||||
needs.verify-changes.outputs.is_docs_change == 'false' &&
|
||||
needs.verify-changes.outputs.is_ui_change == 'false'
|
||||
uses: ./.github/workflows/test-go.yml
|
||||
with:
|
||||
total-runners: 16
|
||||
env-vars: |
|
||||
{
|
||||
"VAULT_CI_GO_TEST_RACE": 1
|
||||
}
|
||||
extra-flags: '-race'
|
||||
go-arch: amd64
|
||||
go-tags: ${{ needs.setup.outputs.go-tags }}
|
||||
runs-on: ${{ needs.setup.outputs.compute-large }}
|
||||
enterprise: ${{ needs.setup.outputs.enterprise }}
|
||||
name: "race"
|
||||
checkout-ref: ${{ needs.setup.outputs.checkout-ref }}
|
||||
secrets: inherit
|
||||
|
||||
test-go-fips:
|
||||
name: Run Go tests with FIPS configuration
|
||||
# Only run fips on the enterprise repo, and only if it's main or a release branch
|
||||
# (i.e. not a PR), or is a PR with the label "fips"
|
||||
if: |
|
||||
needs.setup.outputs.enterprise == 1 &&
|
||||
needs.verify-changes.outputs.is_docs_change == 'false' &&
|
||||
needs.verify-changes.outputs.is_ui_change == 'false' &&
|
||||
(contains(github.event.pull_request.labels.*.name, 'fips') || github.ref_name == 'main' || startsWith(github.ref_name, 'release/'))
|
||||
needs:
|
||||
- setup
|
||||
- verify-changes
|
||||
uses: ./.github/workflows/test-go.yml
|
||||
with:
|
||||
total-runners: 16
|
||||
env-vars: |
|
||||
{
|
||||
"GOEXPERIMENT": "boringcrypto"
|
||||
}
|
||||
go-arch: amd64
|
||||
go-tags: '${{ needs.setup.outputs.go-tags }},deadlock,cgo,fips,fips_140_2'
|
||||
runs-on: ${{ needs.setup.outputs.compute-large }}
|
||||
enterprise: ${{ needs.setup.outputs.enterprise }}
|
||||
name: "fips"
|
||||
checkout-ref: ${{ needs.setup.outputs.checkout-ref }}
|
||||
secrets: inherit
|
||||
|
||||
test-ui:
|
||||
name: Test UI
|
||||
# The test-ui job is only run on:
|
||||
# - pushes to main and branches starting with "release/"
|
||||
# - PRs where the branch starts with "ui/", "backport/ui/", "merge", or when base branch starts with "release/"
|
||||
# - PRs with the "ui" label on GitHub
|
||||
if: |
|
||||
github.ref_name == 'main' ||
|
||||
startsWith(github.ref_name, 'release/') ||
|
||||
startsWith(github.head_ref, 'ui/') ||
|
||||
startsWith(github.head_ref, 'backport/ui/') ||
|
||||
startsWith(github.head_ref, 'merge') ||
|
||||
contains(github.event.pull_request.labels.*.name, 'ui')
|
||||
needs:
|
||||
- setup
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
runs-on: ${{ fromJSON(needs.setup.outputs.compute-largem) }}
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: ./.github/actions/set-up-go
|
||||
with:
|
||||
github-token: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
|
||||
# Setup node.js without caching to allow running npm install -g yarn (next step)
|
||||
- uses: actions/setup-node@e33196f7422957bea03ed53f6fbb155025ffc7b8 # v3.7.0
|
||||
with:
|
||||
node-version-file: './ui/package.json'
|
||||
- id: install-yarn
|
||||
run: |
|
||||
npm install -g yarn
|
||||
# Setup node.js with caching using the yarn.lock file
|
||||
- uses: actions/setup-node@e33196f7422957bea03ed53f6fbb155025ffc7b8 # v3.7.0
|
||||
with:
|
||||
node-version-file: './ui/package.json'
|
||||
cache: yarn
|
||||
cache-dependency-path: ui/yarn.lock
|
||||
- id: install-browser
|
||||
uses: browser-actions/setup-chrome@c485fa3bab6be59dce18dbc18ef6ab7cbc8ff5f1 # v1.2.0
|
||||
- id: ui-dependencies
|
||||
name: ui-dependencies
|
||||
working-directory: ./ui
|
||||
run: |
|
||||
yarn install --frozen-lockfile
|
||||
npm rebuild node-sass
|
||||
- id: vault-auth
|
||||
name: Authenticate to Vault
|
||||
if: github.repository == 'hashicorp/vault-enterprise'
|
||||
run: vault-auth
|
||||
- id: secrets
|
||||
name: Fetch secrets
|
||||
if: github.repository == 'hashicorp/vault-enterprise'
|
||||
uses: hashicorp/vault-action@130d1f5f4fe645bb6c83e4225c04d64cfb62de6e
|
||||
with:
|
||||
url: ${{ steps.vault-auth.outputs.addr }}
|
||||
caCertificate: ${{ steps.vault-auth.outputs.ca_certificate }}
|
||||
token: ${{ steps.vault-auth.outputs.token }}
|
||||
secrets: |
|
||||
kv/data/github/hashicorp/vault-enterprise/github-token token | PRIVATE_REPO_GITHUB_TOKEN;
|
||||
kv/data/github/hashicorp/vault-enterprise/license license_1 | VAULT_LICENSE;
|
||||
- id: setup-git
|
||||
name: Setup Git
|
||||
if: github.repository == 'hashicorp/vault-enterprise'
|
||||
run: |
|
||||
git config --global url."https://${{ steps.secrets.outputs.PRIVATE_REPO_GITHUB_TOKEN }}@github.com".insteadOf https://github.com
|
||||
- id: build-go-dev
|
||||
name: build-go-dev
|
||||
run: |
|
||||
rm -rf ./pkg
|
||||
mkdir ./pkg
|
||||
|
||||
make ci-bootstrap dev
|
||||
- id: test-ui
|
||||
name: test-ui
|
||||
env:
|
||||
VAULT_LICENSE: ${{ steps.secrets.outputs.VAULT_LICENSE }}
|
||||
run: |
|
||||
export PATH="${PWD}/bin:${PATH}"
|
||||
|
||||
if [ "${{ github.repository }}" == 'hashicorp/vault' ] ; then
|
||||
export VAULT_LICENSE="${{ secrets.VAULT_LICENSE }}"
|
||||
fi
|
||||
|
||||
# Run Ember tests
|
||||
cd ui
|
||||
mkdir -p test-results/qunit
|
||||
yarn test:oss
|
||||
- uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
with:
|
||||
name: test-results-ui
|
||||
path: ui/test-results
|
||||
if: success() || failure()
|
||||
- uses: test-summary/action@62bc5c68de2a6a0d02039763b8c754569df99e3f # TSCCR: no entry for repository "test-summary/action"
|
||||
with:
|
||||
paths: "ui/test-results/qunit/results.xml"
|
||||
show: "fail"
|
||||
if: always()
|
||||
|
||||
tests-completed:
|
||||
needs:
|
||||
- setup
|
||||
- test-go
|
||||
- test-ui
|
||||
if: always()
|
||||
runs-on: ${{ fromJSON(needs.setup.outputs.compute-small) }}
|
||||
steps:
|
||||
- run: |
|
||||
tr -d '\n' <<< '${{ toJSON(needs.*.result) }}' | grep -q -v -E '(failure|cancelled)'
|
||||
|
||||
notify-tests-completed-failures-oss:
|
||||
if: |
|
||||
always() &&
|
||||
github.repository == 'hashicorp/vault' &&
|
||||
(needs.test-go.result == 'failure' ||
|
||||
needs.test-go-fips.result == 'failure' ||
|
||||
needs.test-go-race.result == 'failure') &&
|
||||
(github.ref_name == 'main' || startsWith(github.ref_name, 'release/'))
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
needs:
|
||||
- test-go
|
||||
- test-go-fips
|
||||
- test-go-race
|
||||
steps:
|
||||
- name: send-notification
|
||||
uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0
|
||||
# We intentionally aren't using the following here since it's from an internal repo
|
||||
# uses: hashicorp/cloud-gha-slack-notifier@730a033037b8e603adf99ebd3085f0fdfe75e2f4 #v1
|
||||
env:
|
||||
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
|
||||
with:
|
||||
channel-id: "C05AABYEA9Y" # sent to #feed-vault-ci-official
|
||||
payload: |
|
||||
{"text":"OSS test failures on ${{ github.ref_name }}","blocks":[{"type":"header","text":{"type":"plain_text","text":":rotating_light: OSS test failures :rotating_light:","emoji":true}},{"type":"divider"},{"type":"section","text":{"type":"mrkdwn","text":"test(s) failed on ${{ github.ref_name }}"},"accessory":{"type":"button","text":{"type":"plain_text","text":"View Failing Workflow","emoji":true},"url":"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"}}]}
|
||||
|
||||
notify-tests-completed-failures-ent:
|
||||
if: |
|
||||
always() &&
|
||||
github.repository == 'hashicorp/vault-enterprise' &&
|
||||
(needs.test-go.result == 'failure' ||
|
||||
needs.test-go-fips.result == 'failure' ||
|
||||
needs.test-go-race.result == 'failure') &&
|
||||
(github.ref_name == 'main' || startsWith(github.ref_name, 'release/'))
|
||||
runs-on: ['self-hosted', 'linux', 'small']
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
needs:
|
||||
- test-go
|
||||
- test-go-fips
|
||||
- test-go-race
|
||||
steps:
|
||||
- id: vault-auth
|
||||
name: Vault Authenticate
|
||||
run: vault-auth
|
||||
- id: secrets
|
||||
name: Fetch Vault Secrets
|
||||
uses: hashicorp/vault-action@130d1f5f4fe645bb6c83e4225c04d64cfb62de6e
|
||||
with:
|
||||
url: ${{ steps.vault-auth.outputs.addr }}
|
||||
caCertificate: ${{ steps.vault-auth.outputs.ca_certificate }}
|
||||
token: ${{ steps.vault-auth.outputs.token }}
|
||||
secrets: |
|
||||
kv/data/github/${{ github.repository }}/github_actions_notifications_bot token | SLACK_BOT_TOKEN;
|
||||
- name: send-notification
|
||||
uses: hashicorp/cloud-gha-slack-notifier@730a033037b8e603adf99ebd3085f0fdfe75e2f4 #v1
|
||||
with:
|
||||
channel-id: "C05AABYEA9Y" # sent to #feed-vault-ci-official
|
||||
slack-bot-token: ${{ steps.secrets.outputs.SLACK_BOT_TOKEN }}
|
||||
payload: |
|
||||
{"text":"Enterprise test failures on ${{ github.ref_name }}","blocks":[{"type":"header","text":{"type":"plain_text","text":":rotating_light: Enterprise test failures :rotating_light:","emoji":true}},{"type":"divider"},{"type":"section","text":{"type":"mrkdwn","text":"test(s) failed on ${{ github.ref_name }}"},"accessory":{"type":"button","text":{"type":"plain_text","text":"View Failing Workflow","emoji":true},"url":"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"}}]}
|
||||
|
||||
test-summary:
|
||||
name: Go test failures
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
always() &&
|
||||
(needs.test-go.result == 'success' ||
|
||||
needs.test-go.result == 'failure' ||
|
||||
needs.test-go-fips.result == 'success' ||
|
||||
needs.test-go-fips.result == 'failure' ||
|
||||
needs.test-go-race.result == 'success' ||
|
||||
needs.test-go-race.result == 'failure')
|
||||
needs:
|
||||
- test-go
|
||||
- test-go-fips
|
||||
- test-go-race
|
||||
steps:
|
||||
- name: Download failure summary
|
||||
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
|
||||
with:
|
||||
name: failure-summary
|
||||
- name: Prepare failure summary
|
||||
run: |
|
||||
# Sort all of the summary table rows and push them to a temp file.
|
||||
temp_file_name=temp-$(date +%s)
|
||||
cat failure-summary-*.md | sort >> "$temp_file_name"
|
||||
|
||||
# If there are test failures, present them in a format of a GitHub Markdown table.
|
||||
if [ -s "$temp_file_name" ]; then
|
||||
# shellcheck disable=SC2129
|
||||
# Here we create the headings for the summary table
|
||||
echo "| Test Type | Package | Test | Elapsed | Runner Index | Logs |" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "| --------- | ------- | ---- | ------- | ------------ | ---- |" >> "$GITHUB_STEP_SUMMARY"
|
||||
# shellcheck disable=SC2002
|
||||
cat "$temp_file_name" >> "$GITHUB_STEP_SUMMARY"
|
||||
else
|
||||
echo "### All Go tests passed! :white_check_mark:" >> "$GITHUB_STEP_SUMMARY"
|
||||
fi
|
||||
|
||||
# the random EOF is needed for a multiline environment variable
|
||||
EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
|
||||
# shellcheck disable=SC2129
|
||||
echo "TABLE_TEST_RESULTS<<$EOF" >> "$GITHUB_ENV"
|
||||
cat "$temp_file_name" >> "$GITHUB_ENV"
|
||||
echo "$EOF" >> "$GITHUB_ENV"
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- name: Create comment
|
||||
if: github.head_ref != ''
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
RUN_ID: ${{ github.run_id }}
|
||||
REPO: ${{ github.event.repository.name }}
|
||||
TABLE_DATA: ${{ env.TABLE_TEST_RESULTS }}
|
||||
run: ./.github/scripts/report_failed_tests.sh
|
|
@ -0,0 +1,74 @@
|
|||
name: Run linters
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release/**
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.head_ref || github.run_id }}-lint
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
deprecations:
|
||||
name: Deprecated functions
|
||||
runs-on: ubuntu-latest
|
||||
if: github.base_ref == 'main'
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/set-up-go
|
||||
with:
|
||||
github-token: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
|
||||
- run: make ci-deprecations
|
||||
name: Check deprecations
|
||||
|
||||
codechecker:
|
||||
name: Code checks
|
||||
runs-on: ubuntu-latest
|
||||
if: github.base_ref == 'main'
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/set-up-go
|
||||
with:
|
||||
github-token: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
|
||||
# Note: if there is a function we want to ignore the nilnil check for,
|
||||
# You can add 'ignore-nil-nil-function-check' somewhere in the
|
||||
# godoc for the function.
|
||||
- run: make ci-vet-codechecker
|
||||
name: Check custom linters
|
||||
|
||||
format:
|
||||
name: Format
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: ./.github/actions/set-up-go
|
||||
with:
|
||||
github-token: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
|
||||
- name: Go format
|
||||
run: |
|
||||
make ci-bootstrap
|
||||
echo "Using gofumpt version $(go run mvdan.cc/gofumpt -version)"
|
||||
make fmt
|
||||
if ! git diff --exit-code; then
|
||||
echo "Code has formatting errors. Run 'make fmt' to fix"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
semgrep:
|
||||
name: Semgrep
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: returntocorp/semgrep@sha256:ffc6f3567654f9431456d49fd059dfe548f007c494a7eb6cd5a1a3e50d813fb3
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- name: Run Semgrep Rules
|
||||
id: semgrep
|
||||
run: semgrep ci --include '*.go' --config 'tools/semgrep/ci'
|
|
@ -0,0 +1,53 @@
|
|||
---
|
||||
name: lint-enos
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- enos/**
|
||||
|
||||
jobs:
|
||||
metadata:
|
||||
# Only run this workflow on pull requests from hashicorp/vault branches
|
||||
# as we need secrets to install enos.
|
||||
if: "! github.event.pull_request.head.repo.fork"
|
||||
name: metadata
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
runs-on: ${{ steps.metadata.outputs.runs-on }}
|
||||
version: ${{ steps.metadata.outputs.version }}
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- id: set-product-version
|
||||
uses: hashicorp/actions-set-product-version@v1
|
||||
- id: metadata
|
||||
run: |
|
||||
echo "version=${{ steps.set-product-version.outputs.product-version }}" >> "$GITHUB_OUTPUT"
|
||||
github_repository="${{ github.repository }}"
|
||||
if [ "${github_repository##*/}" == "vault-enterprise" ] ; then
|
||||
echo 'runs-on=["self-hosted","ondemand","linux","type=c6a.4xlarge"]' >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo 'runs-on="custom-linux-xl-vault-latest"' >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
lint:
|
||||
needs: metadata
|
||||
runs-on: ${{ fromJSON(needs.metadata.outputs.runs-on) }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
|
||||
ENOS_VAR_tfc_api_token: ${{ secrets.TF_API_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: hashicorp/setup-terraform@v2
|
||||
with:
|
||||
terraform_wrapper: false
|
||||
- uses: hashicorp/action-setup-enos@v1
|
||||
with:
|
||||
github-token: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
|
||||
- name: Ensure shellcheck is available for linting
|
||||
run: which shellcheck || (sudo apt update && sudo apt install -y shellcheck)
|
||||
- name: lint
|
||||
working-directory: ./enos
|
||||
env:
|
||||
ENOS_VAR_vault_product_version: ${{ needs.metadata.outputs.version }}
|
||||
run: make lint
|
|
@ -0,0 +1,72 @@
|
|||
name: enos-release-testing-oss
|
||||
|
||||
on:
|
||||
repository_dispatch:
|
||||
types:
|
||||
- enos-release-testing-oss
|
||||
- enos-release-testing-oss::*
|
||||
|
||||
jobs:
|
||||
product-metadata:
|
||||
if: ${{ startsWith(github.event.client_payload.payload.branch, 'release/') }}
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
vault-revision: ${{ github.event.client_payload.payload.sha }}
|
||||
vault-version: ${{ github.event.client_payload.payload.version }}
|
||||
vault-version-package: ${{ steps.get-metadata.outputs.vault-version-package }}
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
with:
|
||||
# Check out the repository at the same Git SHA that was used to create
|
||||
# the artifacts to get the correct metadata.
|
||||
ref: ${{ github.event.client_payload.payload.sha }}
|
||||
- id: get-metadata
|
||||
env:
|
||||
VAULT_VERSION: ${{ github.event.client_payload.payload.version }}
|
||||
run: |
|
||||
echo "vault-version-package=$(make ci-get-version-package)" >> "$GITHUB_OUTPUT"
|
||||
- name: Release Artifact Info
|
||||
run: |
|
||||
# shellcheck disable=SC2129
|
||||
echo "__Product:__ ${{ github.event.client_payload.payload.product }}" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "__Version:__ ${{ github.event.client_payload.payload.version }}" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "__Commit:__ ${{ github.event.client_payload.payload.sha }}" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "[Build Workflow](https://github.com/${{github.event.client_payload.payload.org}}/${{github.event.client_payload.payload.repo}}/actions/runs/${{github.event.client_payload.payload.buildworkflowid}})" >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
test:
|
||||
name: Test ${{ matrix.build-artifact-name }}
|
||||
if: ${{ startsWith(github.event.client_payload.payload.branch, 'release/') }}
|
||||
needs: product-metadata
|
||||
uses: ./.github/workflows/test-run-enos-scenario-matrix.yml
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- sample-name: release_ce_linux_amd64_deb
|
||||
build-artifact-name: vault_${{ needs.product-metadata.outputs.vault-version-package }}-1_amd64.deb
|
||||
- sample-name: release_ce_linux_arm64_deb
|
||||
build-artifact-name: vault_${{ needs.product-metadata.outputs.vault-version-package }}-1_arm64.deb
|
||||
- sample-name: release_ce_linux_amd64_rpm
|
||||
build-artifact-name: vault_${{ needs.product-metadata.outputs.vault-version-package }}-1.x86_64.rpm
|
||||
- sample-name: release_ce_linux_arm64_rpm
|
||||
build-artifact-name: vault_${{ needs.product-metadata.outputs.vault-version-package }}-1.aarch64.rpm
|
||||
- sample-name: release_ce_linux_amd64_zip
|
||||
build-artifact-name: vault_${{ needs.product-metadata.outputs.vault-version }}_linux_amd64.zip
|
||||
- sample-name: release_ce_linux_arm64_zip
|
||||
build-artifact-name: vault_${{ needs.product-metadata.outputs.vault-version }}_linux_arm64.zip
|
||||
with:
|
||||
build-artifact-name: ${{ matrix.build-artifact-name }}
|
||||
sample-max: 2
|
||||
sample-name: ${{ matrix.sample-name }}
|
||||
vault-revision: ${{ needs.product-metadata.outputs.vault-revision }}
|
||||
vault-version: ${{ needs.product-metadata.outputs.vault-version }}
|
||||
secrets: inherit
|
||||
|
||||
save-metadata:
|
||||
runs-on: linux
|
||||
if: always()
|
||||
needs: test
|
||||
steps:
|
||||
- name: Persist metadata
|
||||
uses: hashicorp/actions-persist-metadata@v1
|
|
@ -0,0 +1,113 @@
|
|||
---
|
||||
name: enos-k8s
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
artifact-build-date:
|
||||
required: false
|
||||
type: string
|
||||
artifact-name:
|
||||
required: true
|
||||
type: string
|
||||
artifact-revision:
|
||||
required: true
|
||||
type: string
|
||||
artifact-version:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
ARTIFACT_BUILD_DATE: ${{ inputs.artifact-build-date }}
|
||||
ARTIFACT_NAME: ${{ inputs.artifact-name }}
|
||||
ARTIFACT_REVISION: ${{ inputs.artifact-revision }}
|
||||
ARTIFACT_VERSION: ${{ inputs.artifact-version }}
|
||||
|
||||
jobs:
|
||||
enos:
|
||||
name: Integration
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- name: Set up Terraform
|
||||
uses: hashicorp/setup-terraform@v2
|
||||
with:
|
||||
# the Terraform wrapper will break Terraform execution in Enos because
|
||||
# it changes the output to text when we expect it to be JSON.
|
||||
terraform_wrapper: false
|
||||
- name: Set up Enos
|
||||
uses: hashicorp/action-setup-enos@v1
|
||||
with:
|
||||
github-token: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
|
||||
- name: Download Docker Image
|
||||
id: download
|
||||
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
|
||||
with:
|
||||
name: ${{ inputs.artifact-name }}
|
||||
path: ./enos/support/downloads
|
||||
- name: Prepare for scenario execution
|
||||
env:
|
||||
IS_ENT: ${{ startsWith(env.ARTIFACT_NAME, 'vault-enterprise' ) }}
|
||||
run: |
|
||||
mkdir -p ./enos/support/terraform-plugin-cache
|
||||
if [ "$IS_ENT" == true ]; then
|
||||
echo "${{ secrets.VAULT_LICENSE }}" > ./enos/support/vault.hclic || true
|
||||
echo "edition=ent" >> "$GITHUB_ENV"
|
||||
echo "edition set to 'ent'"
|
||||
echo "image_repo=hashicorp/vault-enterprise" >> "$GITHUB_ENV"
|
||||
echo "image repo set to 'hashicorp/vault-enterprise'"
|
||||
else
|
||||
echo "edition=ce" >> "$GITHUB_ENV"
|
||||
echo "edition set to 'ce'"
|
||||
echo "image_repo=hashicorp/vault" >> "$GITHUB_ENV"
|
||||
echo "image repo set to 'hashicorp/vault'"
|
||||
fi
|
||||
- name: Run Enos scenario
|
||||
id: run
|
||||
# Continue once and retry to handle occasional blips when creating
|
||||
# infrastructure.
|
||||
continue-on-error: true
|
||||
env:
|
||||
ENOS_VAR_tfc_api_token: ${{ secrets.TF_API_TOKEN }}
|
||||
ENOS_VAR_terraform_plugin_cache_dir: ../support/terraform-plugin-cache
|
||||
ENOS_VAR_vault_build_date: ${{ env.ARTIFACT_BUILD_DATE }}
|
||||
ENOS_VAR_vault_product_version: ${{ env.ARTIFACT_VERSION }}
|
||||
ENOS_VAR_vault_product_revision: ${{ env.ARTIFACT_REVISION }}
|
||||
ENOS_VAR_vault_docker_image_archive: ${{steps.download.outputs.download-path}}/${{ env.ARTIFACT_NAME }}
|
||||
ENOS_VAR_vault_image_repository: ${{ env.image_repo }}
|
||||
run: |
|
||||
enos scenario run --timeout 10m0s --chdir ./enos/k8s edition:${{ env.edition }}
|
||||
- name: Retry Enos scenario
|
||||
id: run_retry
|
||||
if: steps.run.outcome == 'failure'
|
||||
env:
|
||||
ENOS_VAR_tfc_api_token: ${{ secrets.TF_API_TOKEN }}
|
||||
ENOS_VAR_terraform_plugin_cache_dir: ../support/terraform-plugin-cache
|
||||
ENOS_VAR_vault_build_date: ${{ env.ARTIFACT_BUILD_DATE }}
|
||||
ENOS_VAR_vault_product_version: ${{ env.ARTIFACT_VERSION }}
|
||||
ENOS_VAR_vault_product_revision: ${{ env.ARTIFACT_REVISION }}
|
||||
ENOS_VAR_vault_docker_image_archive: ${{steps.download.outputs.download-path}}/${{ env.ARTIFACT_NAME }}
|
||||
ENOS_VAR_vault_image_repository: ${{ env.image_repo }}
|
||||
run: |
|
||||
enos scenario run --timeout 10m0s --chdir ./enos/k8s edition:${{ env.edition }}
|
||||
- name: Destroy Enos scenario
|
||||
if: ${{ always() }}
|
||||
env:
|
||||
ENOS_VAR_tfc_api_token: ${{ secrets.TF_API_TOKEN }}
|
||||
ENOS_VAR_terraform_plugin_cache_dir: ./support/terraform-plugin-cache
|
||||
ENOS_VAR_vault_build_date: ${{ env.ARTIFACT_BUILD_DATE }}
|
||||
ENOS_VAR_vault_product_version: ${{ env.ARTIFACT_VERSION }}
|
||||
ENOS_VAR_vault_product_revision: ${{ env.ARTIFACT_REVISION }}
|
||||
ENOS_VAR_vault_docker_image_archive: ${{steps.download.outputs.download-path}}
|
||||
ENOS_VAR_vault_image_repository: ${{ env.image_repo }}
|
||||
run: |
|
||||
enos scenario destroy --timeout 10m0s --chdir ./enos/k8s edition:${{ env.edition }}
|
||||
- name: Cleanup Enos runtime directories
|
||||
if: ${{ always() }}
|
||||
run: |
|
||||
rm -rf /tmp/enos*
|
||||
rm -rf ./enos/support
|
||||
rm -rf ./enos/k8s/.enos
|
|
@ -0,0 +1,23 @@
|
|||
# This workflow checks that there is either a 'pr/no-milestone' label applied to a PR
|
||||
# or there is a milestone associated with a PR
|
||||
|
||||
name: Check Milestone
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
# milestoned and demilestoned work (https://github.com/github/docs/issues/23909) but they aren't listed in the github documentation
|
||||
types: [opened, synchronize, labeled, unlabeled, milestoned, demilestoned]
|
||||
# Runs on PRs to main and release branches
|
||||
branches:
|
||||
- main
|
||||
- release/**
|
||||
|
||||
jobs:
|
||||
# checks that a milestone entry is present for a PR
|
||||
milestone-check:
|
||||
# If there is a `pr/no-milestone` label we ignore this check
|
||||
if: "!contains(github.event.pull_request.labels.*.name, 'pr/no-milestone')"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check milestone
|
||||
run: ${{ github.event.pull_request.milestone != null }}
|
|
@ -0,0 +1,128 @@
|
|||
# Open Source Community Workflows
|
||||
|
||||
name: Project triage
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened]
|
||||
# Runs on PRs to main
|
||||
branches:
|
||||
- main
|
||||
|
||||
issues:
|
||||
types: [opened, reopened]
|
||||
|
||||
jobs:
|
||||
add-to-projects:
|
||||
# exclude internal PRs
|
||||
if: github.event.pull_request.head.repo.owner.login != 'hashicorp' && ((github.event.action == 'reopened') || (github.event.action == 'opened'))
|
||||
name: Add issue or PR to projects
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- if: github.event.pull_request != null
|
||||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- if: github.event.pull_request != null
|
||||
uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50 # v2.11.1
|
||||
id: changes
|
||||
with:
|
||||
# derived from CODEOWNERS
|
||||
filters: |
|
||||
cryptosec:
|
||||
- 'builtin/logical/pki/**'
|
||||
- 'builtin/logical/ssh/**'
|
||||
- 'builtin/logical/totp/**'
|
||||
- 'builtin/logical/transit/**'
|
||||
ecosystem:
|
||||
- 'builtin/credential/aws/**'
|
||||
- 'builtin/credential/github/**'
|
||||
- 'builtin/credential/ldap/**'
|
||||
- 'builtin/credential/okta/**'
|
||||
- 'builtin/logical/aws/**'
|
||||
- 'builtin/logical/cassandra/**'
|
||||
- 'builtin/logical/consul/**'
|
||||
- 'builtin/logical/database/**'
|
||||
- 'builtin/logical/mongodb/**'
|
||||
- 'builtin/logical/mssql/**'
|
||||
- 'builtin/logical/mysql/**'
|
||||
- 'builtin/logical/nomad/**'
|
||||
- 'builtin/logical/postgresql/**'
|
||||
- 'builtin/logical/rabbitmq/**'
|
||||
- 'command/agent/**'
|
||||
- 'plugins/**'
|
||||
- 'vault/plugin_catalog.go'
|
||||
- 'ui/app/components/auth-jwt.js'
|
||||
- 'ui/app/routes/vault/cluster/oidc-*.js'
|
||||
devex:
|
||||
- 'api/**'
|
||||
- 'command/**'
|
||||
ui:
|
||||
- 'ui/**'
|
||||
|
||||
- name: "Default to core board"
|
||||
run: echo "PROJECT=170" >> "$GITHUB_ENV"
|
||||
- if: github.event.pull_request != null && steps.changes.outputs.cryptosec == 'true'
|
||||
run: echo "PROJECT=172" >> "$GITHUB_ENV"
|
||||
- if: github.event.pull_request != null && steps.changes.outputs.ecosystem == 'true'
|
||||
run: echo "PROJECT=169" >> "$GITHUB_ENV"
|
||||
- if: github.event.pull_request != null && steps.changes.outputs.devex == 'true'
|
||||
run: echo "PROJECT=176" >> "$GITHUB_ENV"
|
||||
- if: github.event.pull_request != null && steps.changes.outputs.ui == 'true'
|
||||
run: echo "PROJECT=171" >> "$GITHUB_ENV"
|
||||
|
||||
- uses: actions/add-to-project@a9f041ddd462ed185893ea1024cec954f50dbe42 # v0.3.0 # TSCCR: no entry for repository "actions/add-to-project"
|
||||
with:
|
||||
project-url: https://github.com/orgs/hashicorp/projects/${{ env.PROJECT }}
|
||||
github-token: ${{ secrets.TRIAGE_GITHUB_TOKEN }}
|
||||
|
||||
# example of something more complicated: deleting an issue or PR automatically (though this is done in the project workflows already)
|
||||
# we have to use the GraphQL API for anything involving projects.
|
||||
#
|
||||
# get-project:
|
||||
# name: Get project data
|
||||
# runs-on: ubuntu-latest
|
||||
# if: github.event.action == 'closed' || github.event.action == 'deleted'
|
||||
# outputs:
|
||||
# project_id: ${{ steps.get-project.outputs.project_id }}
|
||||
# steps:
|
||||
# - id: get-project
|
||||
# name: Get project data
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.TRIAGE_GITHUB_TOKEN }}
|
||||
# ORGANIZATION: hashicorp
|
||||
# PROJECT_NUMBER: 169
|
||||
# run: |
|
||||
# gh api graphql -f query='
|
||||
# query($org: String!, $number: Int!) {
|
||||
# organization(login: $org){
|
||||
# projectV2(number: $number) {
|
||||
# id
|
||||
# }
|
||||
# }
|
||||
# }' -f org=$ORGANIZATION -F number=$PROJECT_NUMBER > project_data.json
|
||||
# echo "::set-output name=project_id::$(jq '.data.organization.projectV2.id' project_data.json)"
|
||||
|
||||
# delete-from-project:
|
||||
# name: Remove issue or PR from project
|
||||
# needs: [get-project]
|
||||
# if: github.event.action == 'closed' || github.event.action == 'deleted'
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - name: Remove issue or PR
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.TRIAGE_GITHUB_TOKEN }}
|
||||
# run: |
|
||||
# PROJECT_ID=${{ needs.get-project.outputs.project_id }}
|
||||
# item_id=${{ github.event.issue.node_id }}
|
||||
# if [ -z "$item_id" ]; then
|
||||
# item_id=${{ github.event.pull_request.node_id }}
|
||||
# fi
|
||||
# gh api graphql -f query='
|
||||
# mutation($project_id: ID!, $item_id: ID!) {
|
||||
# deleteProjectV2Item(
|
||||
# input: {
|
||||
# projectId: $project_id
|
||||
# itemId: $item_id
|
||||
# }
|
||||
# ) {
|
||||
# deletedItemId
|
||||
# }
|
||||
# }' -f project_id=$PROJECT_ID -f item_id=$item_id || true
|
|
@ -0,0 +1,19 @@
|
|||
name: Autoremove Labels
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [closed]
|
||||
pull_request_target:
|
||||
types: [closed]
|
||||
|
||||
jobs:
|
||||
|
||||
RemoveWaitingLabelFromClosedIssueOrPR:
|
||||
if: github.event.action == 'closed'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Remove triaging labels from closed issues and PRs
|
||||
uses: actions-ecosystem/action-remove-labels@2ce5d41b4b6aa8503e285553f75ed56e0a40bae0 # v1.3.0
|
||||
with:
|
||||
labels: |
|
||||
waiting-for-response
|
|
@ -0,0 +1,85 @@
|
|||
name: Security Scan
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches:
|
||||
- 'main'
|
||||
- '!oss-merge-main*'
|
||||
|
||||
jobs:
|
||||
scan:
|
||||
runs-on: ['linux', 'large']
|
||||
if: ${{ github.actor != 'dependabot[bot]' || github.actor != 'hc-github-team-secure-vault-core' }}
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1
|
||||
with:
|
||||
cache: false # save cache space for vault builds: https://github.com/hashicorp/vault/pull/21764
|
||||
go-version-file: .go-version
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@bd6b4b6205c4dbad673328db7b31b7fab9e241c0 # v4.6.1
|
||||
with:
|
||||
python-version: 3.x
|
||||
|
||||
- name: Clone Security Scanner repo
|
||||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
with:
|
||||
repository: hashicorp/security-scanner
|
||||
token: ${{ secrets.HASHIBOT_PRODSEC_GITHUB_TOKEN }}
|
||||
path: security-scanner
|
||||
ref: 52d94588851f38a416f11c1e727131b3c8b0dd4d
|
||||
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
mkdir "$HOME/.bin"
|
||||
cd "$GITHUB_WORKSPACE/security-scanner/pkg/sdk/examples/scan-plugin-semgrep"
|
||||
go build -o scan-plugin-semgrep .
|
||||
mv scan-plugin-semgrep "$HOME/.bin"
|
||||
|
||||
cd "$GITHUB_WORKSPACE/security-scanner/pkg/sdk/examples/scan-plugin-codeql"
|
||||
go build -o scan-plugin-codeql .
|
||||
mv scan-plugin-codeql "$HOME/.bin"
|
||||
|
||||
# Semgrep
|
||||
python3 -m pip install semgrep
|
||||
|
||||
# CodeQL
|
||||
LATEST=$(gh release list --repo https://github.com/github/codeql-action | cut -f 3 | sort --version-sort | tail -n1)
|
||||
gh release download --repo https://github.com/github/codeql-action --pattern codeql-bundle-linux64.tar.gz "$LATEST"
|
||||
tar xf codeql-bundle-linux64.tar.gz -C "$HOME/.bin"
|
||||
|
||||
# Add to PATH
|
||||
echo "$HOME/.bin" >> "$GITHUB_PATH"
|
||||
echo "$HOME/.bin/codeql" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Scan
|
||||
id: scan
|
||||
uses: ./security-scanner
|
||||
# env:
|
||||
# Note: this _should_ work, but causes some issues with Semgrep.
|
||||
# Instead, rely on filtering in the SARIF Output step.
|
||||
#SEMGREP_BASELINE_REF: ${{ github.base_ref }}
|
||||
with:
|
||||
repository: "$PWD"
|
||||
cache-build: true
|
||||
cache-go-modules: false
|
||||
|
||||
- name: SARIF Output
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
cat results.sarif
|
||||
|
||||
- name: Upload SARIF file
|
||||
uses: github/codeql-action/upload-sarif@46a6823b81f2d7c67ddf123851eea88365bc8a67 # codeql-bundle-v2.13.5
|
||||
with:
|
||||
sarif_file: results.sarif
|
|
@ -0,0 +1,21 @@
|
|||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- closed
|
||||
|
||||
jobs:
|
||||
stable_website_cherry_pick:
|
||||
if: github.event.pull_request.merged && contains(github.event.pull_request.labels.*.name, 'docs-cherrypick')
|
||||
runs-on: ubuntu-latest
|
||||
name: Cherry pick to stable-website branch
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
with:
|
||||
ref: stable-website
|
||||
- run: |
|
||||
git fetch --no-tags --prune origin main
|
||||
git config --local user.email "action@github.com"
|
||||
git config --local user.name "GitHub Action"
|
||||
git cherry-pick ${{ github.sha }}
|
||||
git push origin stable-website
|
|
@ -0,0 +1,31 @@
|
|||
name: test-go-acceptance-nightly
|
||||
|
||||
on:
|
||||
# Change to nightly cadence once API-credential-requiring tests are added to the jobs
|
||||
workflow_dispatch:
|
||||
|
||||
# Currently the jobs here are only for acceptance tests that have no dependencies except for Docker
|
||||
jobs:
|
||||
plugins-database:
|
||||
uses: ./.github/workflows/test-run-acc-tests-for-path.yml
|
||||
strategy:
|
||||
matrix:
|
||||
name: [mongodb, mysql, postgresql]
|
||||
with:
|
||||
name: plugins-database-${{ matrix.name }}
|
||||
path: plugins/database/${{ matrix.name }}
|
||||
|
||||
external:
|
||||
uses: ./.github/workflows/test-run-acc-tests-for-path.yml
|
||||
strategy:
|
||||
matrix:
|
||||
name: [api, identity, token]
|
||||
with:
|
||||
name: external-${{ matrix.name }}
|
||||
path: vault/external_tests/${{ matrix.name }}
|
||||
|
||||
# Suggestions and tips for adding more acceptance test jobs:
|
||||
# - the job name is up to you, but it should be derived from the path that the tests are found in
|
||||
# - for instance, "plugins-database" is a job for acceptance tests in the plugins/database path
|
||||
# - the path will be used with go test wildcards, but don't include the preceding "./" or following "/..."
|
||||
# - the name parameter is used to construct the log artifact's name, make it something that is related to the path
|
|
@ -0,0 +1,51 @@
|
|||
name: test-ci-bootstrap
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- enos/ci/**
|
||||
- .github/workflows/test-ci-bootstrap.yml
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- enos/ci/**
|
||||
- .github/workflows/test-ci-bootstrap.yml
|
||||
|
||||
jobs:
|
||||
bootstrap-ci:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
TF_WORKSPACE: "${{ github.event.repository.name }}-ci-enos-bootstrap"
|
||||
TF_VAR_repository: ${{ github.event.repository.name }}
|
||||
TF_VAR_aws_ssh_public_key: ${{ secrets.SSH_KEY_PUBLIC_CI }}
|
||||
TF_TOKEN_app_terraform_io: ${{ secrets.TF_API_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- name: Set up Terraform
|
||||
uses: hashicorp/setup-terraform@v2
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_CI }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_CI }}
|
||||
aws-region: us-east-1
|
||||
role-to-assume: ${{ secrets.AWS_ROLE_ARN_CI }}
|
||||
role-skip-session-tagging: true
|
||||
role-duration-seconds: 3600
|
||||
- name: Init Terraform
|
||||
id: tf_init
|
||||
run: |
|
||||
terraform -chdir=enos/ci/bootstrap init
|
||||
- name: Plan Terraform
|
||||
id: tf_plan
|
||||
run: |
|
||||
terraform -chdir=enos/ci/bootstrap plan
|
||||
- name: Apply Terraform
|
||||
if: ${{ github.ref == 'refs/heads/main' }}
|
||||
id: tf_apply
|
||||
run: |
|
||||
terraform -chdir=enos/ci/bootstrap apply -auto-approve
|
|
@ -0,0 +1,88 @@
|
|||
name: test-ci-cleanup
|
||||
on:
|
||||
schedule:
|
||||
# * is a special character in YAML so you have to quote this string
|
||||
- cron: '05 02 * * *'
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
regions: ${{steps.setup.outputs.regions}}
|
||||
steps:
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_CI }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_CI }}
|
||||
aws-region: us-east-1
|
||||
role-to-assume: ${{ secrets.AWS_ROLE_ARN_CI }}
|
||||
role-skip-session-tagging: true
|
||||
role-duration-seconds: 3600
|
||||
- name: Get all regions
|
||||
id: setup
|
||||
run: |
|
||||
echo "regions=$(aws ec2 describe-regions --region us-east-1 --output json --query 'Regions[].RegionName' | tr -d '\n ')" >> "$GITHUB_OUTPUT"
|
||||
|
||||
aws-nuke:
|
||||
needs: setup
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: rebuy/aws-nuke
|
||||
options:
|
||||
--user root
|
||||
-t
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ env.AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ env.AWS_SECRET_ACCESS_KEY }}
|
||||
TIME_LIMIT: "72h"
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: Configure AWS credentials
|
||||
id: aws-configure
|
||||
uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_CI }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_CI }}
|
||||
aws-region: us-east-1
|
||||
role-to-assume: ${{ secrets.AWS_ROLE_ARN_CI }}
|
||||
role-skip-session-tagging: true
|
||||
role-duration-seconds: 3600
|
||||
mask-aws-account-id: false
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- name: Configure
|
||||
run: |
|
||||
cp enos/ci/aws-nuke.yml .
|
||||
sed -i "s/ACCOUNT_NUM/${{ steps.aws-configure.outputs.aws-account-id }}/g" aws-nuke.yml
|
||||
sed -i "s/TIME_LIMIT/${TIME_LIMIT}/g" aws-nuke.yml
|
||||
# We don't care if cleanup succeeds or fails, because dependencies be dependenceies,
|
||||
# we'll fail on actually actionable things in the quota steep afterwards.
|
||||
- name: Clean up abandoned resources
|
||||
# Filter STDERR because it's super noisy about things we don't have access to
|
||||
run: |
|
||||
aws-nuke -c aws-nuke.yml -q --no-dry-run --force 2>/tmp/aws-nuke-error.log || true
|
||||
|
||||
check-quotas:
|
||||
needs: [ setup, aws-nuke ]
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: jantman/awslimitchecker
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ env.AWS_ACCESS_KEY_ID_CI }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ env.AWS_SECRET_ACCESS_KEY_CI }}
|
||||
strategy:
|
||||
matrix:
|
||||
region: ${{ fromJSON(needs.setup.outputs.regions) }}
|
||||
steps:
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_CI }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_CI }}
|
||||
aws-region: us-east-1
|
||||
role-to-assume: ${{ secrets.AWS_ROLE_ARN_CI }}
|
||||
role-skip-session-tagging: true
|
||||
role-duration-seconds: 3600
|
||||
# Currently just checking VPC limits across all region, can add more checks here in future
|
||||
- name: Check AWS Quotas
|
||||
run: awslimitchecker -S "VPC" -r ${{matrix.region}}
|
|
@ -0,0 +1,145 @@
|
|||
---
|
||||
name: Vault UI Tests
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
test_filter:
|
||||
type: string
|
||||
description: "A filter to limit the ui tests to. Will be appended to the ember test command as '-f=<filter>'"
|
||||
required: false
|
||||
storage_backend:
|
||||
type: string
|
||||
description: "The storage backend to use, either 'raft' or 'consul'"
|
||||
default: raft
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
test_filter:
|
||||
type: string
|
||||
description: "A filter to limit the ui tests to. Will be appended to the ember test command as '-f=<filter>'"
|
||||
required: false
|
||||
storage_backend:
|
||||
description: "The storage backend to use, either 'raft' or 'consul'"
|
||||
required: true
|
||||
default: raft
|
||||
type: choice
|
||||
options:
|
||||
- raft
|
||||
- consul
|
||||
|
||||
jobs:
|
||||
get-metadata:
|
||||
name: Get metadata
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
runs-on: ${{ steps.get-metadata.outputs.runs-on }}
|
||||
vault_edition: ${{ steps.get-metadata.outputs.vault_edition }}
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- id: get-metadata
|
||||
env:
|
||||
IS_ENT: ${{ startsWith(github.event.repository.name, 'vault-enterprise' ) }}
|
||||
run: |
|
||||
if [ "$IS_ENT" == true ]; then
|
||||
echo "detected vault_edition=ent"
|
||||
echo "runs-on=['self-hosted', 'ondemand', 'os=linux', 'type=m5d.4xlarge']" >> "$GITHUB_OUTPUT"
|
||||
echo "vault_edition=ent" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "detected vault_edition=oss"
|
||||
echo "runs-on=\"custom-linux-xl-vault-latest\"" >> "$GITHUB_OUTPUT"
|
||||
echo "vault_edition=oss" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
run-ui-tests:
|
||||
name: Run UI Tests
|
||||
needs: get-metadata
|
||||
runs-on: ${{ fromJSON(needs.get-metadata.outputs.runs-on) }}
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
|
||||
# Pass in enos variables
|
||||
ENOS_VAR_aws_region: us-east-1
|
||||
ENOS_VAR_aws_ssh_keypair_name: ${{ github.event.repository.name }}-ci-ssh-key
|
||||
ENOS_VAR_aws_ssh_private_key_path: ./support/private_key.pem
|
||||
ENOS_VAR_tfc_api_token: ${{ secrets.TF_API_TOKEN }}
|
||||
ENOS_VAR_terraform_plugin_cache_dir: ./support/terraform-plugin-cache
|
||||
ENOS_VAR_vault_license_path: ./support/vault.hclic
|
||||
GOPRIVATE: github.com/hashicorp
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: ./.github/actions/set-up-go
|
||||
with:
|
||||
github-token: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
|
||||
- uses: hashicorp/action-setup-enos@v1
|
||||
with:
|
||||
github-token: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
|
||||
- name: Set Up Git
|
||||
run: git config --global url."https://${{ secrets.elevated_github_token }}:@github.com".insteadOf "https://github.com"
|
||||
- name: Set Up Node
|
||||
uses: actions/setup-node@e33196f7422957bea03ed53f6fbb155025ffc7b8 # v3.7.0
|
||||
with:
|
||||
node-version-file: './ui/package.json'
|
||||
- name: Set Up Terraform
|
||||
uses: hashicorp/setup-terraform@v2
|
||||
with:
|
||||
cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}
|
||||
terraform_wrapper: false
|
||||
- name: Prepare scenario dependencies
|
||||
run: |
|
||||
mkdir -p ./enos/support/terraform-plugin-cache
|
||||
echo "${{ secrets.SSH_KEY_PRIVATE_CI }}" > ./enos/support/private_key.pem
|
||||
chmod 600 ./enos/support/private_key.pem
|
||||
- name: Set Up Vault Enterprise License
|
||||
if: contains(github.event.repository.name, 'ent')
|
||||
run: echo "${{ secrets.VAULT_LICENSE }}" > ./enos/support/vault.hclic || true
|
||||
- name: Check Chrome Installed
|
||||
id: chrome-check
|
||||
run: echo "chrome-version=$(chrome --version 2> /dev/null || google-chrome --version 2> /dev/null || google-chrome-stable --version 2> /dev/null || echo 'not-installed')" >> "$GITHUB_OUTPUT"
|
||||
- name: Install Chrome Dependencies
|
||||
if: steps.chrome-check.outputs.chrome-version == 'not-installed'
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y libnss3-dev libgdk-pixbuf2.0-dev libgtk-3-dev libxss-dev libasound2
|
||||
- name: Install Chrome
|
||||
if: steps.chrome-check.outputs.chrome-version == 'not-installed'
|
||||
uses: browser-actions/setup-chrome@c485fa3bab6be59dce18dbc18ef6ab7cbc8ff5f1 # v1.2.0
|
||||
- name: Installed Chrome Version
|
||||
run: |
|
||||
echo "Installed Chrome Version = [$(chrome --version 2> /dev/null || google-chrome --version 2> /dev/null || google-chrome-stable --version 2> /dev/null)]"
|
||||
- name: Configure AWS credentials from Test account
|
||||
uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_CI }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_CI }}
|
||||
aws-region: us-east-1
|
||||
role-to-assume: ${{ secrets.AWS_ROLE_ARN_CI }}
|
||||
role-skip-session-tagging: true
|
||||
role-duration-seconds: 3600
|
||||
- name: Set Up Cluster
|
||||
id: setup_cluster
|
||||
env:
|
||||
ENOS_VAR_ui_run_tests: false
|
||||
# Continue once and retry to handle occasional blips when creating infrastructure.
|
||||
continue-on-error: true
|
||||
run: enos scenario launch --timeout 60m0s --chdir ./enos ui edition:${{ needs.get-metadata.outputs.vault_edition }} backend:${{ inputs.storage_backend }}
|
||||
- name: Retry Set Up Cluster
|
||||
id: setup_cluster_retry
|
||||
if: steps.setup_cluster.outcome == 'failure'
|
||||
env:
|
||||
ENOS_VAR_ui_run_tests: false
|
||||
run: enos scenario launch --timeout 60m0s --chdir ./enos ui edition:${{ needs.get-metadata.outputs.vault_edition }} backend:${{ inputs.storage_backend }}
|
||||
- name: Run UI Tests
|
||||
id: run_ui_tests
|
||||
env:
|
||||
ENOS_VAR_ui_test_filter: "${{ inputs.test_filter }}"
|
||||
run: enos scenario run --timeout 60m0s --chdir ./enos ui edition:${{ needs.get-metadata.outputs.vault_edition }} backend:${{ inputs.storage_backend }}
|
||||
- name: Ensure scenario has been destroyed
|
||||
if: ${{ always() }}
|
||||
run: enos scenario destroy --timeout 60m0s --chdir ./enos ui edition:${{ needs.get-metadata.outputs.vault_edition }} backend:${{ inputs.storage_backend }}
|
||||
- name: Clean up Enos runtime directories
|
||||
if: ${{ always() }}
|
||||
run: |
|
||||
rm -rf /tmp/enos*
|
||||
rm -rf ./enos/support
|
||||
rm -rf ./enos/.enos
|
|
@ -0,0 +1,424 @@
|
|||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
go-arch:
|
||||
description: The execution architecture (arm, amd64, etc.)
|
||||
required: true
|
||||
type: string
|
||||
enterprise:
|
||||
description: A flag indicating if this workflow is executing for the enterprise repository.
|
||||
required: true
|
||||
type: string
|
||||
total-runners:
|
||||
description: Number of runners to use for executing non-binary tests.
|
||||
required: true
|
||||
type: string
|
||||
binary-tests:
|
||||
description: Whether to run the binary tests.
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
env-vars:
|
||||
description: A map of environment variables as JSON.
|
||||
required: false
|
||||
type: string
|
||||
default: '{}'
|
||||
extra-flags:
|
||||
description: A space-separated list of additional build flags.
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
runs-on:
|
||||
description: An expression indicating which kind of runners to use.
|
||||
required: false
|
||||
type: string
|
||||
default: ubuntu-latest
|
||||
go-tags:
|
||||
description: A comma-separated list of additional build tags to consider satisfied during the build.
|
||||
required: false
|
||||
type: string
|
||||
name:
|
||||
description: A suffix to append to archived test results
|
||||
required: false
|
||||
default: ''
|
||||
type: string
|
||||
go-test-parallelism:
|
||||
description: The parallelism parameter for Go tests
|
||||
required: false
|
||||
default: 20
|
||||
type: number
|
||||
timeout-minutes:
|
||||
description: The maximum number of minutes that this workflow should run
|
||||
required: false
|
||||
default: 60
|
||||
type: number
|
||||
testonly:
|
||||
description: Whether to run the tests tagged with testonly.
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
checkout-ref:
|
||||
description: The ref to use for checkout.
|
||||
required: false
|
||||
default: ${{ github.ref }}
|
||||
type: string
|
||||
|
||||
env: ${{ fromJSON(inputs.env-vars) }}
|
||||
|
||||
jobs:
|
||||
test-matrix:
|
||||
permissions:
|
||||
id-token: write # Note: this permission is explicitly required for Vault auth
|
||||
contents: read
|
||||
runs-on: ${{ fromJSON(inputs.runs-on) }}
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
with:
|
||||
ref: ${{ inputs.checkout-ref }}
|
||||
- uses: ./.github/actions/set-up-go
|
||||
with:
|
||||
github-token: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
|
||||
- name: Authenticate to Vault
|
||||
id: vault-auth
|
||||
if: github.repository == 'hashicorp/vault-enterprise'
|
||||
run: vault-auth
|
||||
- name: Fetch Secrets
|
||||
id: secrets
|
||||
if: github.repository == 'hashicorp/vault-enterprise'
|
||||
uses: hashicorp/vault-action@130d1f5f4fe645bb6c83e4225c04d64cfb62de6e
|
||||
with:
|
||||
url: ${{ steps.vault-auth.outputs.addr }}
|
||||
caCertificate: ${{ steps.vault-auth.outputs.ca_certificate }}
|
||||
token: ${{ steps.vault-auth.outputs.token }}
|
||||
secrets: |
|
||||
kv/data/github/${{ github.repository }}/datadog-ci DATADOG_API_KEY;
|
||||
kv/data/github/${{ github.repository }}/github-token username-and-token | github-token;
|
||||
kv/data/github/${{ github.repository }}/license license_1 | VAULT_LICENSE_CI;
|
||||
kv/data/github/${{ github.repository }}/license license_2 | VAULT_LICENSE_2;
|
||||
kv/data/github/${{ github.repository }}/hcp-link HCP_API_ADDRESS;
|
||||
kv/data/github/${{ github.repository }}/hcp-link HCP_AUTH_URL;
|
||||
kv/data/github/${{ github.repository }}/hcp-link HCP_CLIENT_ID;
|
||||
kv/data/github/${{ github.repository }}/hcp-link HCP_CLIENT_SECRET;
|
||||
kv/data/github/${{ github.repository }}/hcp-link HCP_RESOURCE_ID;
|
||||
- id: setup-git-private
|
||||
name: Setup Git configuration (private)
|
||||
if: github.repository == 'hashicorp/vault-enterprise'
|
||||
run: |
|
||||
git config --global url."https://${{ steps.secrets.outputs.github-token }}@github.com".insteadOf https://github.com
|
||||
- id: setup-git-public
|
||||
name: Setup Git configuration (public)
|
||||
if: github.repository != 'hashicorp/vault-enterprise'
|
||||
run: |
|
||||
git config --global url."https://${{ secrets.ELEVATED_GITHUB_TOKEN}}@github.com".insteadOf https://github.com
|
||||
- uses: ./.github/actions/set-up-gotestsum
|
||||
- run: mkdir -p test-results/go-test
|
||||
- uses: actions/cache/restore@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
|
||||
with:
|
||||
path: test-results/go-test
|
||||
key: go-test-reports-${{ github.run_number }}
|
||||
restore-keys: go-test-reports-
|
||||
- name: List cached results
|
||||
id: list-cached-results
|
||||
run: ls -lhR test-results/go-test
|
||||
- name: Build matrix excluding binary, integration, and testonly tests
|
||||
id: build-non-binary
|
||||
if: ${{ !inputs.testonly }}
|
||||
env:
|
||||
GOPRIVATE: github.com/hashicorp/*
|
||||
run: |
|
||||
# testonly tests need additional build tag though let's exclude them anyway for clarity
|
||||
(
|
||||
go list ./... | grep -v "_binary" | grep -v "vault/integ" | grep -v "testonly" | gotestsum tool ci-matrix --debug \
|
||||
--partitions "${{ inputs.total-runners }}" \
|
||||
--timing-files 'test-results/go-test/*.json' > matrix.json
|
||||
)
|
||||
- name: Build matrix for tests tagged with testonly
|
||||
if: ${{ inputs.testonly }}
|
||||
env:
|
||||
GOPRIVATE: github.com/hashicorp/*
|
||||
run: |
|
||||
set -exo pipefail
|
||||
# enable glob expansion
|
||||
shopt -s nullglob
|
||||
# testonly tagged tests need an additional tag to be included
|
||||
# also running some extra tests for sanity checking with the testonly build tag
|
||||
(
|
||||
go list -tags=testonly ./vault/external_tests/{kv,token,*replication-perf*,*testonly*} ./vault/ | gotestsum tool ci-matrix --debug \
|
||||
--partitions "${{ inputs.total-runners }}" \
|
||||
--timing-files 'test-results/go-test/*.json' > matrix.json
|
||||
)
|
||||
# disable glob expansion
|
||||
shopt -u nullglob
|
||||
- name: Capture list of binary tests
|
||||
if: inputs.binary-tests
|
||||
id: list-binary-tests
|
||||
run: |
|
||||
LIST="$(go list ./... | grep "_binary" | xargs)"
|
||||
echo "list=$LIST" >> "$GITHUB_OUTPUT"
|
||||
- name: Build complete matrix
|
||||
id: build
|
||||
run: |
|
||||
set -exo pipefail
|
||||
matrix_file="matrix.json"
|
||||
if [ "${{ inputs.binary-tests}}" == "true" ] && [ -n "${{ steps.list-binary-tests.outputs.list }}" ]; then
|
||||
export BINARY_TESTS="${{ steps.list-binary-tests.outputs.list }}"
|
||||
jq --arg BINARY "${BINARY_TESTS}" --arg BINARY_INDEX "${{ inputs.total-runners }}" \
|
||||
'.include += [{
|
||||
"id": $BINARY_INDEX,
|
||||
"estimatedRuntime": "N/A",
|
||||
"packages": $BINARY,
|
||||
"description": "partition $BINARY_INDEX - binary test packages"
|
||||
}]' matrix.json > new-matrix.json
|
||||
matrix_file="new-matrix.json"
|
||||
fi
|
||||
# convert the json to a map keyed by id
|
||||
(
|
||||
echo -n "matrix="
|
||||
jq -c \
|
||||
'.include | map( { (.id|tostring): . } ) | add' "$matrix_file"
|
||||
) >> "$GITHUB_OUTPUT"
|
||||
# extract an array of ids from the json
|
||||
(
|
||||
echo -n "matrix_ids="
|
||||
jq -c \
|
||||
'[ .include[].id | tostring ]' "$matrix_file"
|
||||
) >> "$GITHUB_OUTPUT"
|
||||
outputs:
|
||||
matrix: ${{ steps.build.outputs.matrix }}
|
||||
matrix_ids: ${{ steps.build.outputs.matrix_ids }}
|
||||
|
||||
test-go:
|
||||
needs: test-matrix
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
id-token: write # Note: this permission is explicitly required for Vault auth
|
||||
runs-on: ${{ fromJSON(inputs.runs-on) }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
id: ${{ fromJSON(needs.test-matrix.outputs.matrix_ids) }}
|
||||
env:
|
||||
GOPRIVATE: github.com/hashicorp/*
|
||||
TIMEOUT_IN_MINUTES: ${{ inputs.timeout-minutes }}
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
with:
|
||||
ref: ${{ inputs.checkout-ref }}
|
||||
- uses: ./.github/actions/set-up-go
|
||||
with:
|
||||
github-token: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
|
||||
- name: Authenticate to Vault
|
||||
id: vault-auth
|
||||
if: github.repository == 'hashicorp/vault-enterprise'
|
||||
run: vault-auth
|
||||
- name: Fetch Secrets
|
||||
id: secrets
|
||||
if: github.repository == 'hashicorp/vault-enterprise'
|
||||
uses: hashicorp/vault-action@130d1f5f4fe645bb6c83e4225c04d64cfb62de6e
|
||||
with:
|
||||
url: ${{ steps.vault-auth.outputs.addr }}
|
||||
caCertificate: ${{ steps.vault-auth.outputs.ca_certificate }}
|
||||
token: ${{ steps.vault-auth.outputs.token }}
|
||||
secrets: |
|
||||
kv/data/github/${{ github.repository }}/datadog-ci DATADOG_API_KEY;
|
||||
kv/data/github/${{ github.repository }}/github-token username-and-token | github-token;
|
||||
kv/data/github/${{ github.repository }}/license license_1 | VAULT_LICENSE_CI;
|
||||
kv/data/github/${{ github.repository }}/license license_2 | VAULT_LICENSE_2;
|
||||
kv/data/github/${{ github.repository }}/hcp-link HCP_API_ADDRESS;
|
||||
kv/data/github/${{ github.repository }}/hcp-link HCP_AUTH_URL;
|
||||
kv/data/github/${{ github.repository }}/hcp-link HCP_CLIENT_ID;
|
||||
kv/data/github/${{ github.repository }}/hcp-link HCP_CLIENT_SECRET;
|
||||
kv/data/github/${{ github.repository }}/hcp-link HCP_RESOURCE_ID;
|
||||
- id: setup-git-private
|
||||
name: Setup Git configuration (private)
|
||||
if: github.repository == 'hashicorp/vault-enterprise'
|
||||
run: |
|
||||
git config --global url."https://${{ steps.secrets.outputs.github-token }}@github.com".insteadOf https://github.com
|
||||
- id: setup-git-public
|
||||
name: Setup Git configuration (public)
|
||||
if: github.repository != 'hashicorp/vault-enterprise'
|
||||
run: |
|
||||
git config --global url."https://${{ secrets.ELEVATED_GITHUB_TOKEN}}@github.com".insteadOf https://github.com
|
||||
- id: build
|
||||
if: inputs.binary-tests && matrix.id == inputs.total-runners
|
||||
env:
|
||||
GOPRIVATE: github.com/hashicorp/*
|
||||
run: time make ci-bootstrap dev
|
||||
- uses: ./.github/actions/set-up-gotestsum
|
||||
- id: run-go-tests
|
||||
name: Run Go tests
|
||||
timeout-minutes: ${{ fromJSON(env.TIMEOUT_IN_MINUTES) }}
|
||||
env:
|
||||
COMMIT_SHA: ${{ github.sha }}
|
||||
run: |
|
||||
set -exo pipefail
|
||||
|
||||
# Build the dynamically generated source files.
|
||||
make prep
|
||||
|
||||
packages=$(echo "${{ toJSON(needs.test-matrix.outputs.matrix) }}" | jq -c -r --arg id "${{ matrix.id }}" '.[$id] | .packages')
|
||||
|
||||
if [ -z "$packages" ]; then
|
||||
echo "no test packages to run"
|
||||
exit 1
|
||||
fi
|
||||
# We don't want VAULT_LICENSE set when running Go tests, because that's
|
||||
# not what developers have in their environments and it could break some
|
||||
# tests; it would be like setting VAULT_TOKEN. However some non-Go
|
||||
# CI commands, like the UI tests, shouldn't have to worry about licensing.
|
||||
# So we provide the tests which want an externally supplied license with licenses
|
||||
# via the VAULT_LICENSE_CI and VAULT_LICENSE_2 environment variables, and here we unset it.
|
||||
# shellcheck disable=SC2034
|
||||
VAULT_LICENSE=
|
||||
|
||||
# Assign test licenses to relevant variables if they aren't already
|
||||
if [[ ${{ github.repository }} == 'hashicorp/vault' ]]; then
|
||||
export VAULT_LICENSE_CI=${{ secrets.ci_license }}
|
||||
export VAULT_LICENSE_2=${{ secrets.ci_license_2 }}
|
||||
export HCP_API_ADDRESS=${{ secrets.HCP_API_ADDRESS }}
|
||||
export HCP_AUTH_URL=${{ secrets.HCP_AUTH_URL }}
|
||||
export HCP_CLIENT_ID=${{ secrets.HCP_CLIENT_ID }}
|
||||
export HCP_CLIENT_SECRET=${{ secrets.HCP_CLIENT_SECRET }}
|
||||
export HCP_RESOURCE_ID=${{ secrets.HCP_RESOURCE_ID }}
|
||||
# Temporarily removing this variable to cause HCP Link tests
|
||||
# to be skipped.
|
||||
#export HCP_SCADA_ADDRESS=${{ secrets.HCP_SCADA_ADDRESS }}
|
||||
fi
|
||||
|
||||
if [ -f bin/vault ]; then
|
||||
VAULT_BINARY="$(pwd)/bin/vault"
|
||||
export VAULT_BINARY
|
||||
fi
|
||||
|
||||
# On a release branch, add a flag to rerun failed tests
|
||||
# shellcheck disable=SC2193 # can get false positive for this comparision
|
||||
if [[ "${{ github.base_ref }}" == release/* ]] || [[ -z "${{ github.base_ref }}" && "${{ github.ref_name }}" == release/* ]]
|
||||
then
|
||||
RERUN_FAILS="--rerun-fails"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2086 # can't quote RERUN_FAILS
|
||||
GOARCH=${{ inputs.go-arch }} \
|
||||
gotestsum --format=short-verbose \
|
||||
--junitfile test-results/go-test/results-${{ matrix.id }}.xml \
|
||||
--jsonfile test-results/go-test/results-${{ matrix.id }}.json \
|
||||
--jsonfile-timing-events failure-summary-${{ matrix.id }}${{ inputs.name != '' && '-' || '' }}${{ inputs.name }}.json \
|
||||
$RERUN_FAILS \
|
||||
--packages "$packages" \
|
||||
-- \
|
||||
-tags "${{ inputs.go-tags }}" \
|
||||
-timeout=${{ env.TIMEOUT_IN_MINUTES }}m \
|
||||
-parallel=${{ inputs.go-test-parallelism }} \
|
||||
${{ inputs.extra-flags }} \
|
||||
- name: Prepare datadog-ci
|
||||
if: github.repository == 'hashicorp/vault' && (success() || failure())
|
||||
continue-on-error: true
|
||||
run: |
|
||||
curl -L --fail "https://github.com/DataDog/datadog-ci/releases/latest/download/datadog-ci_linux-x64" --output "/usr/local/bin/datadog-ci"
|
||||
chmod +x /usr/local/bin/datadog-ci
|
||||
- name: Upload test results to DataDog
|
||||
continue-on-error: true
|
||||
env:
|
||||
DD_ENV: ci
|
||||
run: |
|
||||
if [[ ${{ github.repository }} == 'hashicorp/vault' ]]; then
|
||||
export DATADOG_API_KEY=${{ secrets.DATADOG_API_KEY }}
|
||||
fi
|
||||
datadog-ci junit upload --service "$GITHUB_REPOSITORY" test-results/go-test/results-${{ matrix.id }}.xml
|
||||
if: success() || failure()
|
||||
- name: Archive test results
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
with:
|
||||
name: test-results${{ inputs.name != '' && '-' || '' }}${{ inputs.name }}
|
||||
path: test-results/go-test
|
||||
if: success() || failure()
|
||||
# GitHub Actions doesn't expose the job ID or the URL to the job execution,
|
||||
# so we have to fetch it from the API
|
||||
- name: Fetch job logs URL
|
||||
uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6.4.1
|
||||
if: success() || failure()
|
||||
continue-on-error: true
|
||||
with:
|
||||
retries: 3
|
||||
script: |
|
||||
// We surround the whole script with a try-catch block, to avoid each of the matrix jobs
|
||||
// displaying an error in the GHA workflow run annotations, which gets very noisy.
|
||||
// If an error occurs, it will be logged so that we don't lose any information about the reason for failure.
|
||||
try {
|
||||
const fs = require("fs");
|
||||
const result = await github.rest.actions.listJobsForWorkflowRun({
|
||||
owner: context.repo.owner,
|
||||
per_page: 100,
|
||||
repo: context.repo.repo,
|
||||
run_id: context.runId,
|
||||
});
|
||||
|
||||
// Determine what job name to use for the query. These values are hardcoded, because GHA doesn't
|
||||
// expose them in any of the contexts available within a workflow run.
|
||||
let prefixToSearchFor;
|
||||
switch ("${{ inputs.name }}") {
|
||||
case "race":
|
||||
prefixToSearchFor = 'Run Go tests with data race detection / test-go (${{ matrix.id }})'
|
||||
break
|
||||
case "fips":
|
||||
prefixToSearchFor = 'Run Go tests with FIPS configuration / test-go (${{ matrix.id }})'
|
||||
break
|
||||
default:
|
||||
prefixToSearchFor = 'Run Go tests / test-go (${{ matrix.id }})'
|
||||
}
|
||||
|
||||
const jobData = result.data.jobs.filter(
|
||||
(job) => job.name.startsWith(prefixToSearchFor)
|
||||
);
|
||||
const url = jobData[0].html_url;
|
||||
const envVarName = "GH_JOB_URL";
|
||||
const envVar = envVarName + "=" + url;
|
||||
const envFile = process.env.GITHUB_ENV;
|
||||
|
||||
fs.appendFile(envFile, envVar, (err) => {
|
||||
if (err) throw err;
|
||||
console.log("Successfully set " + envVarName + " to: " + url);
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("Error: " + error);
|
||||
return
|
||||
}
|
||||
- name: Prepare failure summary
|
||||
if: success() || failure()
|
||||
continue-on-error: true
|
||||
run: |
|
||||
# This jq query filters out successful tests, leaving only the failures.
|
||||
# Then, it formats the results into rows of a Markdown table.
|
||||
# An example row will resemble this:
|
||||
# | github.com/hashicorp/vault/package | TestName | fips | 0 | 2 | [view results](github.com/link-to-logs) |
|
||||
jq -r -n 'inputs
|
||||
| select(.Action == "fail")
|
||||
| "| ${{inputs.name}} | \(.Package) | \(.Test // "-") | \(.Elapsed) | ${{ matrix.id }} | [view test results :scroll:](${{ env.GH_JOB_URL }}) |"' \
|
||||
failure-summary-${{ matrix.id }}${{ inputs.name != '' && '-' || '' }}${{inputs.name}}.json \
|
||||
>> failure-summary-${{ matrix.id }}${{ inputs.name != '' && '-' || '' }}${{inputs.name}}.md
|
||||
- name: Upload failure summary
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
if: success() || failure()
|
||||
with:
|
||||
name: failure-summary
|
||||
path: failure-summary-${{ matrix.id }}${{ inputs.name != '' && '-' || '' }}${{inputs.name}}.md
|
||||
|
||||
test-collect-reports:
|
||||
if: ${{ ! cancelled() }}
|
||||
needs: test-go
|
||||
runs-on: ${{ fromJSON(inputs.runs-on) }}
|
||||
steps:
|
||||
- uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
|
||||
with:
|
||||
path: test-results/go-test
|
||||
key: go-test-reports-${{ github.run_number }}
|
||||
restore-keys: go-test-reports-
|
||||
- uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
|
||||
with:
|
||||
name: test-results
|
||||
path: test-results/go-test
|
||||
- run: |
|
||||
ls -lhR test-results/go-test
|
||||
find test-results/go-test -mindepth 1 -mtime +3 -delete
|
||||
ls -lhR test-results/go-test
|
|
@ -0,0 +1,32 @@
|
|||
name: test-run-go-tests-for-path
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
name:
|
||||
description: 'The name to use that will appear in the output log file artifact'
|
||||
required: true
|
||||
type: string
|
||||
path:
|
||||
description: 'The path to the test without the precedeing "./" or following "/..." e.g. go test -v ./$path/...'
|
||||
required: true
|
||||
type: string
|
||||
# We will need to add the capacity for receiving passed secrets once we get to the tests that require API credentials
|
||||
|
||||
env:
|
||||
VAULT_ACC: 1
|
||||
|
||||
jobs:
|
||||
go-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: ./.github/actions/set-up-go
|
||||
with:
|
||||
github-token: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
|
||||
- run: go test -v ./${{ inputs.path }}/... 2>&1 | tee ${{ inputs.name }}.txt
|
||||
- uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
with:
|
||||
name: ${{ inputs.name }}-output
|
||||
path: ${{ inputs.name }}.txt
|
||||
retention-days: 2
|
|
@ -0,0 +1,197 @@
|
|||
---
|
||||
name: enos
|
||||
|
||||
on:
|
||||
# Only trigger this working using workflow_call. This workflow requires many
|
||||
# secrets that must be inherited from the caller workflow.
|
||||
workflow_call:
|
||||
inputs:
|
||||
# The name of the artifact that we're going to use for testing. This should
|
||||
# match exactly to build artifacts uploaded to Github and Artifactory.
|
||||
build-artifact-name:
|
||||
required: true
|
||||
type: string
|
||||
# The maximum number of scenarios to include in the test sample.
|
||||
sample-max:
|
||||
default: 1
|
||||
type: number
|
||||
# The name of the enos scenario sample that defines compatible scenarios we can
|
||||
# can test with.
|
||||
sample-name:
|
||||
required: true
|
||||
type: string
|
||||
runs-on:
|
||||
# NOTE: The value should be JSON encoded as that's the only way we can
|
||||
# pass arrays with workflow_call.
|
||||
type: string
|
||||
required: false
|
||||
default: '"ubuntu-latest"'
|
||||
ssh-key-name:
|
||||
type: string
|
||||
default: ${{ github.event.repository.name }}-ci-ssh-key
|
||||
vault-edition:
|
||||
required: false
|
||||
type: string
|
||||
default: oss
|
||||
# The Git commit SHA used as the revision when building vault
|
||||
vault-revision:
|
||||
required: true
|
||||
type: string
|
||||
vault-version:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
metadata:
|
||||
runs-on: ${{ fromJSON(inputs.runs-on) }}
|
||||
outputs:
|
||||
build-date: ${{ steps.metadata.outputs.build-date }}
|
||||
sample: ${{ steps.metadata.outputs.sample }}
|
||||
vault-version: ${{ steps.metadata.outputs.vault-version }}
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
with:
|
||||
ref: ${{ inputs.vault-revision }}
|
||||
- uses: hashicorp/action-setup-enos@v1
|
||||
with:
|
||||
github-token: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
|
||||
- id: metadata
|
||||
run: |
|
||||
echo "build-date=$(make ci-get-date)" >> "$GITHUB_OUTPUT"
|
||||
sample="$(enos scenario sample observe ${{ inputs.sample-name }} --chdir ./enos --min 1 --max ${{ inputs.sample-max }} --seed "$(date +%s%N)" --format json | jq -c ".observation.elements")"
|
||||
echo "sample=$sample"
|
||||
echo "sample=$sample" >> "$GITHUB_OUTPUT"
|
||||
if [[ "${{ inputs.vault-edition }}" == "oss" ]]; then
|
||||
echo "vault-version=${{ inputs.vault-version }}" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
# shellcheck disable=2001
|
||||
vault_version="$(sed 's/+ent/+${{ inputs.vault-edition }}/g' <<< '${{ inputs.vault-version }}')"
|
||||
echo "vault-version=$vault_version"
|
||||
echo "vault-version=$vault_version" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
# Run the Enos test scenario(s)
|
||||
run:
|
||||
needs: metadata
|
||||
name: run ${{ matrix.scenario.id.filter }}
|
||||
strategy:
|
||||
fail-fast: false # don't fail as that can skip required cleanup steps for jobs
|
||||
matrix:
|
||||
include: ${{ fromJSON(needs.metadata.outputs.sample) }}
|
||||
runs-on: ${{ fromJSON(inputs.runs-on) }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
|
||||
# Pass in enos variables
|
||||
ENOS_VAR_aws_region: ${{ matrix.attributes.aws_region }}
|
||||
ENOS_VAR_aws_ssh_keypair_name: ${{ inputs.ssh-key-name }}
|
||||
ENOS_VAR_aws_ssh_private_key_path: ./support/private_key.pem
|
||||
ENOS_VAR_tfc_api_token: ${{ secrets.TF_API_TOKEN }}
|
||||
ENOS_VAR_artifactory_username: ${{ secrets.ARTIFACTORY_USER }}
|
||||
ENOS_VAR_artifactory_token: ${{ secrets.ARTIFACTORY_TOKEN }}
|
||||
ENOS_VAR_terraform_plugin_cache_dir: ./support/terraform-plugin-cache
|
||||
ENOS_VAR_vault_artifact_path: ./support/downloads/${{ inputs.build-artifact-name }}
|
||||
ENOS_VAR_vault_build_date: ${{ needs.metadata.outputs.build-date }}
|
||||
ENOS_VAR_vault_product_version: ${{ needs.metadata.outputs.vault-version }}
|
||||
ENOS_VAR_vault_revision: ${{ inputs.vault-revision }}
|
||||
ENOS_VAR_vault_license_path: ./support/vault.hclic
|
||||
ENOS_DEBUG_DATA_ROOT_DIR: /tmp/enos-debug-data
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: hashicorp/setup-terraform@v2
|
||||
with:
|
||||
# the Terraform wrapper will break Terraform execution in Enos because
|
||||
# it changes the output to text when we expect it to be JSON.
|
||||
terraform_wrapper: false
|
||||
- uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_CI }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_CI }}
|
||||
aws-region: ${{ matrix.attributes.aws_region }}
|
||||
role-to-assume: ${{ secrets.AWS_ROLE_ARN_CI }}
|
||||
role-skip-session-tagging: true
|
||||
role-duration-seconds: 3600
|
||||
- uses: hashicorp/action-setup-enos@v1
|
||||
with:
|
||||
github-token: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
|
||||
- name: Prepare scenario dependencies
|
||||
id: prepare_scenario
|
||||
run: |
|
||||
mkdir -p "./enos/support/terraform-plugin-cache"
|
||||
echo "${{ secrets.SSH_KEY_PRIVATE_CI }}" > "./enos/support/private_key.pem"
|
||||
chmod 600 "./enos/support/private_key.pem"
|
||||
echo "debug_data_artifact_name=enos-debug-data_$(echo "${{ matrix.scenario }}" | sed -e 's/ /_/g' | sed -e 's/:/=/g')" >> "$GITHUB_OUTPUT"
|
||||
- if: contains(inputs.sample-name, 'build')
|
||||
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
|
||||
with:
|
||||
name: ${{ inputs.build-artifact-name }}
|
||||
path: ./enos/support/downloads
|
||||
- if: contains(inputs.sample-name, 'ent')
|
||||
name: Configure Vault license
|
||||
run: echo "${{ secrets.VAULT_LICENSE }}" > ./enos/support/vault.hclic || true
|
||||
- id: launch
|
||||
name: enos scenario launch ${{ matrix.scenario.id.filter }}
|
||||
# Continue once and retry to handle occasional blips when creating infrastructure.
|
||||
continue-on-error: true
|
||||
run: enos scenario launch --timeout 60m0s --chdir ./enos ${{ matrix.scenario.id.filter }}
|
||||
- if: steps.launch.outcome == 'failure'
|
||||
id: launch_retry
|
||||
name: Retry enos scenario launch ${{ matrix.scenario.id.filter }}
|
||||
run: enos scenario launch --timeout 60m0s --chdir ./enos ${{ matrix.scenario.id.filter }}
|
||||
- name: Upload Debug Data
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
with:
|
||||
# The name of the artifact is the same as the matrix scenario name with the spaces replaced with underscores and colons replaced by equals.
|
||||
name: ${{ steps.prepare_scenario.outputs.debug_data_artifact_name }}
|
||||
path: ${{ env.ENOS_DEBUG_DATA_ROOT_DIR }}
|
||||
retention-days: 30
|
||||
continue-on-error: true
|
||||
- if: ${{ always() }}
|
||||
id: destroy
|
||||
name: enos scenario destroy ${{ matrix.scenario.id.filter }}
|
||||
continue-on-error: true
|
||||
run: enos scenario destroy --timeout 60m0s --chdir ./enos ${{ matrix.scenario.id.filter }}
|
||||
- if: steps.destroy.outcome == 'failure'
|
||||
id: destroy_retry
|
||||
name: Retry enos scenario destroy ${{ matrix.scenario.id.filter }}
|
||||
continue-on-error: true
|
||||
run: enos scenario destroy --timeout 60m0s --chdir ./enos ${{ matrix.scenario.id.filter }}
|
||||
- name: Clean up Enos runtime directories
|
||||
id: cleanup
|
||||
if: ${{ always() }}
|
||||
continue-on-error: true
|
||||
run: |
|
||||
rm -rf /tmp/enos*
|
||||
rm -rf ./enos/support
|
||||
rm -rf ./enos/.enos
|
||||
# Send slack notifications to #feed-vault-enos-failures any of our enos scenario commands fail.
|
||||
# There is an incoming webhook set up on the "Enos Vault Failure Bot" Slackbot:
|
||||
# https://api.slack.com/apps/A05E31CH1LG/incoming-webhooks
|
||||
- if: ${{ always() && ! cancelled() }}
|
||||
name: Notify launch failed
|
||||
uses: hashicorp/actions-slack-status@v1
|
||||
with:
|
||||
failure-message: "enos scenario launch ${{ matrix.scenario.id.filter}} failed. \nTriggering event: `${{ github.event_name }}` \nActor: `${{ github.actor }}`"
|
||||
status: ${{ steps.launch.outcome }}
|
||||
slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
- if: ${{ always() && ! cancelled() }}
|
||||
name: Notify retry launch failed
|
||||
uses: hashicorp/actions-slack-status@v1
|
||||
with:
|
||||
failure-message: "retry enos scenario launch ${{ matrix.scenario.id.filter}} failed. \nTriggering event: `${{ github.event_name }}` \nActor: `${{ github.actor }}`"
|
||||
status: ${{ steps.launch_retry.outcome }}
|
||||
slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
- if: ${{ always() && ! cancelled() }}
|
||||
name: Notify destroy failed
|
||||
uses: hashicorp/actions-slack-status@v1
|
||||
with:
|
||||
failure-message: "enos scenario destroy ${{ matrix.scenario.id.filter}} failed. \nTriggering event: `${{ github.event_name }}` \nActor: `${{ github.actor }}`"
|
||||
status: ${{ steps.destroy.outcome }}
|
||||
slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
- if: ${{ always() && ! cancelled() }}
|
||||
name: Notify retry destroy failed
|
||||
uses: hashicorp/actions-slack-status@v1
|
||||
with:
|
||||
failure-message: "retry enos scenario destroy ${{ matrix.scenario.id.filter}} failed. \nTriggering event: `${{ github.event_name }}` \nActor: `${{ github.actor }}`"
|
||||
status: ${{ steps.destroy_retry.outcome }}
|
||||
slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
|
@ -0,0 +1,31 @@
|
|||
name: verify_changes
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
outputs:
|
||||
is_docs_change:
|
||||
description: "determines if the changes contains docs"
|
||||
value: ${{ jobs.verify-doc-ui-changes.outputs.is_docs_change }}
|
||||
is_ui_change:
|
||||
description: "determines if the changes contain ui"
|
||||
value: ${{ jobs.verify-doc-ui-changes.outputs.is_ui_change }}
|
||||
|
||||
jobs:
|
||||
# verify-doc-ui-changes determines if the changes are only for docs (website) and/or ui
|
||||
verify-doc-ui-changes:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
is_docs_change: ${{ steps.get-changeddir.outputs.is_docs_change }}
|
||||
is_ui_change: ${{ steps.get-changeddir.outputs.is_ui_change }}
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
fetch-depth: 0 # Use fetch depth 0 for comparing changes to base branch
|
||||
- name: Get changed directories
|
||||
id: get-changeddir
|
||||
env:
|
||||
TYPE: ${{ github.event_name }}
|
||||
REF_NAME: ${{ github.ref_name }}
|
||||
BASE: ${{ github.base_ref }}
|
||||
run: ./.github/scripts/verify_changes.sh ${{ env.TYPE }} ${{ env.REF_NAME }} ${{ env.BASE }}
|
|
@ -0,0 +1,133 @@
|
|||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
.cover
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
# Other dirs
|
||||
/bin/
|
||||
/pkg/
|
||||
|
||||
# Generated Web UI goes here
|
||||
/http/web_ui/*.*
|
||||
/http/web_ui/**/*.*
|
||||
|
||||
# Vault-specific
|
||||
example.hcl
|
||||
example.vault.d
|
||||
|
||||
# Without this, the *.[568vq] above ignores this folder.
|
||||
!**/graphrbac/1.6
|
||||
|
||||
# Ruby
|
||||
website/vendor
|
||||
website/.bundle
|
||||
website/build
|
||||
website/tmp
|
||||
|
||||
# Vagrant
|
||||
.vagrant/
|
||||
Vagrantfile
|
||||
|
||||
# Configs
|
||||
*.hcl
|
||||
!.copywrite.hcl
|
||||
!.release/ci.hcl
|
||||
!.release/security-scan.hcl
|
||||
!.release/linux/package/etc/vault.d/vault.hcl
|
||||
!command/agent/config/test-fixtures/*.hcl
|
||||
!command/server/test-fixtures/**/*.hcl
|
||||
!enos/**/*.hcl
|
||||
|
||||
# Enos
|
||||
.enos
|
||||
enos-local.vars.hcl
|
||||
enos/**/support
|
||||
enos/**/kubeconfig
|
||||
.terraform
|
||||
.terraform.lock.hcl
|
||||
.tfstate.*
|
||||
|
||||
.DS_Store
|
||||
.idea
|
||||
.vscode
|
||||
|
||||
dist/*
|
||||
|
||||
# ignore ctags
|
||||
./tags
|
||||
|
||||
# Editor backups
|
||||
*~
|
||||
*.sw[a-z]
|
||||
|
||||
# IntelliJ IDEA project files
|
||||
.idea
|
||||
*.ipr
|
||||
*.iml
|
||||
|
||||
# compiled output
|
||||
ui/dist
|
||||
ui/tmp
|
||||
ui/root
|
||||
|
||||
# dependencies
|
||||
ui/node_modules
|
||||
ui/bower_components
|
||||
|
||||
# misc
|
||||
ui/.DS_Store
|
||||
ui/.sass-cache
|
||||
ui/connect.lock
|
||||
ui/coverage/*
|
||||
ui/libpeerconnection.log
|
||||
ui/npm-debug.log
|
||||
ui/test-reports/*
|
||||
ui/testem.log
|
||||
|
||||
# used for JS acceptance tests
|
||||
ui/tests/helpers/vault-keys.js
|
||||
ui/vault-ui-integration-server.pid
|
||||
|
||||
# for building static assets
|
||||
node_modules
|
||||
|
||||
# Website
|
||||
website/.bundle
|
||||
website/build/
|
||||
website/npm-debug.log
|
||||
website/vendor
|
||||
website/.bundle
|
||||
website/.cache
|
||||
website/assets/node_modules
|
||||
website/assets/public
|
||||
website/components/node_modules
|
||||
|
||||
.buildcache/
|
||||
.releaser/
|
||||
*.log
|
||||
|
||||
tools/godoctests/.bin
|
||||
tools/gonilnilfunctions/.bin
|
||||
tools/codechecker/.bin
|
||||
.ci-bootstrap
|
|
@ -0,0 +1 @@
|
|||
1.20.11
|
|
@ -0,0 +1,78 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# READ THIS BEFORE MAKING CHANGES:
|
||||
#
|
||||
# If you want to add a new pre-commit check, here are the rules:
|
||||
#
|
||||
# 1. Create a bash function for your check (see e.g. ui_lint below).
|
||||
# NOTE: Each function will be called in a sub-shell so you can freely
|
||||
# change directory without worrying about interference.
|
||||
# 2. Add the name of the function to the CHECKS variable.
|
||||
# 3. If no changes relevant to your new check are staged, then
|
||||
# do not output anything at all - this would be annoying noise.
|
||||
# In this case, call 'return 0' from your check function to return
|
||||
# early without blocking the commit.
|
||||
# 4. If any non-trivial check-specific thing has to be invoked,
|
||||
# then output '==> [check description]' as the first line of
|
||||
# output. Each sub-check should output '--> [subcheck description]'
|
||||
# after it has run, indicating success or failure.
|
||||
# 5. Call 'block [reason]' to block the commit. This ensures the last
|
||||
# line of output calls out that the commit was blocked - which may not
|
||||
# be obvious from random error messages generated in 4.
|
||||
#
|
||||
# At the moment, there are no automated tests for this hook, so please run it
|
||||
# locally to check you have not broken anything - breaking this will interfere
|
||||
# with other peoples' workflows significantly, so be sure, check everything twice.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Call block to block the commit with a message.
|
||||
block() {
|
||||
echo "$@"
|
||||
echo "Commit blocked - see errors above."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Add all check functions to this space separated list.
|
||||
# They are executed in this order (see end of file).
|
||||
CHECKS="ui_lint backend_lint"
|
||||
|
||||
# Run ui linter if changes in that dir detected.
|
||||
ui_lint() {
|
||||
local DIR=ui LINTER=node_modules/.bin/lint-staged
|
||||
|
||||
# Silently succeed if no changes staged for $DIR
|
||||
if git diff --name-only --cached --exit-code -- $DIR/; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Silently succeed if the linter has not been installed.
|
||||
# We assume that if you're doing UI dev, you will have installed the linter
|
||||
# by running yarn.
|
||||
if [ ! -x $DIR/$LINTER ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "==> Changes detected in $DIR/: Running linter..."
|
||||
|
||||
# Run the linter from the UI dir.
|
||||
cd $DIR
|
||||
$LINTER || block "UI lint failed"
|
||||
}
|
||||
|
||||
backend_lint() {
|
||||
# Silently succeed if no changes staged for Go code files.
|
||||
staged=$(git diff --name-only --cached --exit-code -- '*.go')
|
||||
ret=$?
|
||||
if [ $ret -eq 0 ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Only run fmtcheck on staged files
|
||||
./scripts/gofmtcheck.sh "${staged}" || block "Backend linting failed; run 'make fmt' to fix."
|
||||
}
|
||||
|
||||
for CHECK in $CHECKS; do
|
||||
# Force each check into a subshell to avoid crosstalk.
|
||||
( $CHECK ) || exit $?
|
||||
done
|
|
@ -0,0 +1,23 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
remote="$1"
|
||||
remote_url=$(git remote get-url $remote)
|
||||
|
||||
if [[ $remote_url == *"vault-enterprise"* ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$remote" = "enterprise" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$remote" = "ent" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -f command/version_ent.go ]; then
|
||||
echo "Found enterprise version file while pushing to oss remote"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,176 @@
|
|||
# Copyright (c) HashiCorp, Inc.
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
schema = "1"
|
||||
|
||||
project "vault" {
|
||||
team = "vault"
|
||||
slack {
|
||||
notification_channel = "C03RXFX5M4L" // #feed-vault-releases
|
||||
}
|
||||
github {
|
||||
organization = "hashicorp"
|
||||
repository = "vault"
|
||||
release_branches = [
|
||||
"main",
|
||||
"release/**",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
event "merge" {
|
||||
// "entrypoint" to use if build is not run automatically
|
||||
// i.e. send "merge" complete signal to orchestrator to trigger build
|
||||
}
|
||||
|
||||
event "build" {
|
||||
depends = ["merge"]
|
||||
action "build" {
|
||||
organization = "hashicorp"
|
||||
repository = "vault"
|
||||
workflow = "build"
|
||||
}
|
||||
}
|
||||
|
||||
event "prepare" {
|
||||
depends = ["build"]
|
||||
action "prepare" {
|
||||
organization = "hashicorp"
|
||||
repository = "crt-workflows-common"
|
||||
workflow = "prepare"
|
||||
depends = ["build"]
|
||||
}
|
||||
|
||||
notification {
|
||||
on = "fail"
|
||||
}
|
||||
}
|
||||
|
||||
event "enos-release-testing-oss" {
|
||||
depends = ["prepare"]
|
||||
action "enos-release-testing-oss" {
|
||||
organization = "hashicorp"
|
||||
repository = "vault"
|
||||
workflow = "enos-release-testing-oss"
|
||||
}
|
||||
|
||||
notification {
|
||||
on = "fail"
|
||||
}
|
||||
}
|
||||
|
||||
## These events are publish and post-publish events and should be added to the end of the file
|
||||
## after the verify event stanza.
|
||||
|
||||
event "trigger-staging" {
|
||||
// This event is dispatched by the bob trigger-promotion command
|
||||
// and is required - do not delete.
|
||||
}
|
||||
|
||||
event "promote-staging" {
|
||||
depends = ["trigger-staging"]
|
||||
action "promote-staging" {
|
||||
organization = "hashicorp"
|
||||
repository = "crt-workflows-common"
|
||||
workflow = "promote-staging"
|
||||
config = "release-metadata.hcl"
|
||||
}
|
||||
|
||||
notification {
|
||||
on = "always"
|
||||
}
|
||||
}
|
||||
|
||||
event "promote-staging-docker" {
|
||||
depends = ["promote-staging"]
|
||||
action "promote-staging-docker" {
|
||||
organization = "hashicorp"
|
||||
repository = "crt-workflows-common"
|
||||
workflow = "promote-staging-docker"
|
||||
}
|
||||
|
||||
notification {
|
||||
on = "always"
|
||||
}
|
||||
}
|
||||
|
||||
event "trigger-production" {
|
||||
// This event is dispatched by the bob trigger-promotion command
|
||||
// and is required - do not delete.
|
||||
}
|
||||
|
||||
event "promote-production" {
|
||||
depends = ["trigger-production"]
|
||||
action "promote-production" {
|
||||
organization = "hashicorp"
|
||||
repository = "crt-workflows-common"
|
||||
workflow = "promote-production"
|
||||
}
|
||||
|
||||
notification {
|
||||
on = "always"
|
||||
}
|
||||
}
|
||||
|
||||
event "promote-production-docker" {
|
||||
depends = ["promote-production"]
|
||||
action "promote-production-docker" {
|
||||
organization = "hashicorp"
|
||||
repository = "crt-workflows-common"
|
||||
workflow = "promote-production-docker"
|
||||
}
|
||||
|
||||
notification {
|
||||
on = "always"
|
||||
}
|
||||
}
|
||||
|
||||
event "promote-production-packaging" {
|
||||
depends = ["promote-production-docker"]
|
||||
action "promote-production-packaging" {
|
||||
organization = "hashicorp"
|
||||
repository = "crt-workflows-common"
|
||||
workflow = "promote-production-packaging"
|
||||
}
|
||||
|
||||
notification {
|
||||
on = "always"
|
||||
}
|
||||
}
|
||||
|
||||
# The post-publish-website event should not be merged into the enterprise repo.
|
||||
# It is for OSS use only.
|
||||
event "post-publish-website" {
|
||||
depends = ["promote-production-packaging"]
|
||||
action "post-publish-website" {
|
||||
organization = "hashicorp"
|
||||
repository = "crt-workflows-common"
|
||||
workflow = "post-publish-website"
|
||||
}
|
||||
|
||||
notification {
|
||||
on = "always"
|
||||
}
|
||||
}
|
||||
|
||||
event "bump-version" {
|
||||
depends = ["post-publish-website"]
|
||||
action "bump-version" {
|
||||
organization = "hashicorp"
|
||||
repository = "crt-workflows-common"
|
||||
workflow = "bump-version"
|
||||
}
|
||||
}
|
||||
|
||||
event "update-ironbank" {
|
||||
depends = ["bump-version"]
|
||||
action "update-ironbank" {
|
||||
organization = "hashicorp"
|
||||
repository = "crt-workflows-common"
|
||||
workflow = "update-ironbank"
|
||||
}
|
||||
|
||||
notification {
|
||||
on = "fail"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
#!/usr/bin/dumb-init /bin/sh
|
||||
# Copyright (c) HashiCorp, Inc.
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
set -e
|
||||
|
||||
# Note above that we run dumb-init as PID 1 in order to reap zombie processes
|
||||
# as well as forward signals to all processes in its session. Normally, sh
|
||||
# wouldn't do either of these functions so we'd leak zombies as well as do
|
||||
# unclean termination of all our sub-processes.
|
||||
|
||||
# Prevent core dumps
|
||||
ulimit -c 0
|
||||
|
||||
# Allow setting VAULT_REDIRECT_ADDR and VAULT_CLUSTER_ADDR using an interface
|
||||
# name instead of an IP address. The interface name is specified using
|
||||
# VAULT_REDIRECT_INTERFACE and VAULT_CLUSTER_INTERFACE environment variables. If
|
||||
# VAULT_*_ADDR is also set, the resulting URI will combine the protocol and port
|
||||
# number with the IP of the named interface.
|
||||
get_addr () {
|
||||
local if_name=$1
|
||||
local uri_template=$2
|
||||
ip addr show dev $if_name | awk -v uri=$uri_template '/\s*inet\s/ { \
|
||||
ip=gensub(/(.+)\/.+/, "\\1", "g", $2); \
|
||||
print gensub(/^(.+:\/\/).+(:.+)$/, "\\1" ip "\\2", "g", uri); \
|
||||
exit}'
|
||||
}
|
||||
|
||||
if [ -n "$VAULT_REDIRECT_INTERFACE" ]; then
|
||||
export VAULT_REDIRECT_ADDR=$(get_addr $VAULT_REDIRECT_INTERFACE ${VAULT_REDIRECT_ADDR:-"http://0.0.0.0:8200"})
|
||||
echo "Using $VAULT_REDIRECT_INTERFACE for VAULT_REDIRECT_ADDR: $VAULT_REDIRECT_ADDR"
|
||||
fi
|
||||
if [ -n "$VAULT_CLUSTER_INTERFACE" ]; then
|
||||
export VAULT_CLUSTER_ADDR=$(get_addr $VAULT_CLUSTER_INTERFACE ${VAULT_CLUSTER_ADDR:-"https://0.0.0.0:8201"})
|
||||
echo "Using $VAULT_CLUSTER_INTERFACE for VAULT_CLUSTER_ADDR: $VAULT_CLUSTER_ADDR"
|
||||
fi
|
||||
|
||||
# VAULT_CONFIG_DIR isn't exposed as a volume but you can compose additional
|
||||
# config files in there if you use this image as a base, or use
|
||||
# VAULT_LOCAL_CONFIG below.
|
||||
VAULT_CONFIG_DIR=/vault/config
|
||||
|
||||
# You can also set the VAULT_LOCAL_CONFIG environment variable to pass some
|
||||
# Vault configuration JSON without having to bind any volumes.
|
||||
if [ -n "$VAULT_LOCAL_CONFIG" ]; then
|
||||
echo "$VAULT_LOCAL_CONFIG" > "$VAULT_CONFIG_DIR/local.json"
|
||||
fi
|
||||
|
||||
# If the user is trying to run Vault directly with some arguments, then
|
||||
# pass them to Vault.
|
||||
if [ "${1:0:1}" = '-' ]; then
|
||||
set -- vault "$@"
|
||||
fi
|
||||
|
||||
# Look for Vault subcommands.
|
||||
if [ "$1" = 'server' ]; then
|
||||
shift
|
||||
set -- vault server \
|
||||
-config="$VAULT_CONFIG_DIR" \
|
||||
-dev-root-token-id="$VAULT_DEV_ROOT_TOKEN_ID" \
|
||||
-dev-listen-address="${VAULT_DEV_LISTEN_ADDRESS:-"0.0.0.0:8200"}" \
|
||||
"$@"
|
||||
elif [ "$1" = 'version' ]; then
|
||||
# This needs a special case because there's no help output.
|
||||
set -- vault "$@"
|
||||
elif vault --help "$1" 2>&1 | grep -q "vault $1"; then
|
||||
# We can't use the return code to check for the existence of a subcommand, so
|
||||
# we have to use grep to look for a pattern in the help output.
|
||||
set -- vault "$@"
|
||||
fi
|
||||
|
||||
# If we are running Vault, make sure it executes as the proper user.
|
||||
if [ "$1" = 'vault' ]; then
|
||||
if [ -z "$SKIP_CHOWN" ]; then
|
||||
# If the config dir is bind mounted then chown it
|
||||
if [ "$(stat -c %u /vault/config)" != "$(id -u vault)" ]; then
|
||||
chown -R vault:vault /vault/config || echo "Could not chown /vault/config (may not have appropriate permissions)"
|
||||
fi
|
||||
|
||||
# If the logs dir is bind mounted then chown it
|
||||
if [ "$(stat -c %u /vault/logs)" != "$(id -u vault)" ]; then
|
||||
chown -R vault:vault /vault/logs
|
||||
fi
|
||||
|
||||
# If the file dir is bind mounted then chown it
|
||||
if [ "$(stat -c %u /vault/file)" != "$(id -u vault)" ]; then
|
||||
chown -R vault:vault /vault/file
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$SKIP_SETCAP" ]; then
|
||||
# Allow mlock to avoid swapping Vault memory to disk
|
||||
setcap cap_ipc_lock=+ep $(readlink -f $(which vault))
|
||||
|
||||
# In the case vault has been started in a container without IPC_LOCK privileges
|
||||
if ! vault -version 1>/dev/null 2>/dev/null; then
|
||||
>&2 echo "Couldn't start vault with IPC_LOCK. Disabling IPC_LOCK, please use --cap-add IPC_LOCK"
|
||||
setcap cap_ipc_lock=-ep $(readlink -f $(which vault))
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$(id -u)" = '0' ]; then
|
||||
set -- su-exec vault "$@"
|
||||
fi
|
||||
fi
|
||||
|
||||
exec "$@"
|
|
@ -0,0 +1,116 @@
|
|||
#!/bin/sh
|
||||
# Copyright (c) HashiCorp, Inc.
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
set -e
|
||||
|
||||
# Prevent core dumps
|
||||
ulimit -c 0
|
||||
|
||||
# Allow setting VAULT_REDIRECT_ADDR and VAULT_CLUSTER_ADDR using an interface
|
||||
# name instead of an IP address. The interface name is specified using
|
||||
# VAULT_REDIRECT_INTERFACE and VAULT_CLUSTER_INTERFACE environment variables. If
|
||||
# VAULT_*_ADDR is also set, the resulting URI will combine the protocol and port
|
||||
# number with the IP of the named interface.
|
||||
get_addr () {
|
||||
local if_name=$1
|
||||
local uri_template=$2
|
||||
ip addr show dev $if_name | awk -v uri=$uri_template '/\s*inet\s/ { \
|
||||
ip=gensub(/(.+)\/.+/, "\\1", "g", $2); \
|
||||
print gensub(/^(.+:\/\/).+(:.+)$/, "\\1" ip "\\2", "g", uri); \
|
||||
exit}'
|
||||
}
|
||||
|
||||
if [ -n "$VAULT_REDIRECT_INTERFACE" ]; then
|
||||
export VAULT_REDIRECT_ADDR=$(get_addr $VAULT_REDIRECT_INTERFACE ${VAULT_REDIRECT_ADDR:-"http://0.0.0.0:8200"})
|
||||
echo "Using $VAULT_REDIRECT_INTERFACE for VAULT_REDIRECT_ADDR: $VAULT_REDIRECT_ADDR"
|
||||
fi
|
||||
if [ -n "$VAULT_CLUSTER_INTERFACE" ]; then
|
||||
export VAULT_CLUSTER_ADDR=$(get_addr $VAULT_CLUSTER_INTERFACE ${VAULT_CLUSTER_ADDR:-"https://0.0.0.0:8201"})
|
||||
echo "Using $VAULT_CLUSTER_INTERFACE for VAULT_CLUSTER_ADDR: $VAULT_CLUSTER_ADDR"
|
||||
fi
|
||||
|
||||
# VAULT_CONFIG_DIR isn't exposed as a volume but you can compose additional
|
||||
# config files in there if you use this image as a base, or use
|
||||
# VAULT_LOCAL_CONFIG below.
|
||||
VAULT_CONFIG_DIR=/vault/config
|
||||
|
||||
# You can also set the VAULT_LOCAL_CONFIG environment variable to pass some
|
||||
# Vault configuration JSON without having to bind any volumes.
|
||||
if [ -n "$VAULT_LOCAL_CONFIG" ]; then
|
||||
echo "$VAULT_LOCAL_CONFIG" > "$VAULT_CONFIG_DIR/local.json"
|
||||
fi
|
||||
|
||||
# Due to OpenShift environment compatibility, we have to allow group write
|
||||
# access to the Vault configuration. This requires us to disable the stricter
|
||||
# file permissions checks introduced in Vault v1.11.0.
|
||||
export VAULT_DISABLE_FILE_PERMISSIONS_CHECK=true
|
||||
|
||||
# If the user is trying to run Vault directly with some arguments, then
|
||||
# pass them to Vault.
|
||||
if [ "${1:0:1}" = '-' ]; then
|
||||
set -- vault "$@"
|
||||
fi
|
||||
|
||||
# Look for Vault subcommands.
|
||||
if [ "$1" = 'server' ]; then
|
||||
shift
|
||||
set -- vault server \
|
||||
-config="$VAULT_CONFIG_DIR" \
|
||||
-dev-root-token-id="$VAULT_DEV_ROOT_TOKEN_ID" \
|
||||
-dev-listen-address="${VAULT_DEV_LISTEN_ADDRESS:-"0.0.0.0:8200"}" \
|
||||
"$@"
|
||||
elif [ "$1" = 'version' ]; then
|
||||
# This needs a special case because there's no help output.
|
||||
set -- vault "$@"
|
||||
elif vault --help "$1" 2>&1 | grep -q "vault $1"; then
|
||||
# We can't use the return code to check for the existence of a subcommand, so
|
||||
# we have to use grep to look for a pattern in the help output.
|
||||
set -- vault "$@"
|
||||
fi
|
||||
|
||||
# If we are running Vault, make sure it executes as the proper user.
|
||||
if [ "$1" = 'vault' ]; then
|
||||
if [ -z "$SKIP_CHOWN" ]; then
|
||||
# If the config dir is bind mounted then chown it
|
||||
if [ "$(stat -c %u /vault/config)" != "$(id -u vault)" ]; then
|
||||
chown -R vault:vault /vault/config || echo "Could not chown /vault/config (may not have appropriate permissions)"
|
||||
fi
|
||||
|
||||
# If the logs dir is bind mounted then chown it
|
||||
if [ "$(stat -c %u /vault/logs)" != "$(id -u vault)" ]; then
|
||||
chown -R vault:vault /vault/logs
|
||||
fi
|
||||
|
||||
# If the file dir is bind mounted then chown it
|
||||
if [ "$(stat -c %u /vault/file)" != "$(id -u vault)" ]; then
|
||||
chown -R vault:vault /vault/file
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$SKIP_SETCAP" ]; then
|
||||
# Allow mlock to avoid swapping Vault memory to disk
|
||||
setcap cap_ipc_lock=+ep $(readlink -f /bin/vault)
|
||||
|
||||
# In the case vault has been started in a container without IPC_LOCK privileges
|
||||
if ! vault -version 1>/dev/null 2>/dev/null; then
|
||||
>&2 echo "Couldn't start vault with IPC_LOCK. Disabling IPC_LOCK, please use --cap-add IPC_LOCK"
|
||||
setcap cap_ipc_lock=-ep $(readlink -f /bin/vault)
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# In case of Docker, where swap may be enabled, we
|
||||
# still require mlocking to be available. So this script
|
||||
# was executed as root to make this happen, however,
|
||||
# we're now rerunning the entrypoint script as the Vault
|
||||
# user but no longer need to run setup code for setcap
|
||||
# or chowning directories (previously done on the first run).
|
||||
if [[ "$(id -u)" == '0' ]]
|
||||
then
|
||||
export SKIP_CHOWN="true"
|
||||
export SKIP_SETCAP="true"
|
||||
exec su vault -p "$0" -- "$@"
|
||||
else
|
||||
exec "$@"
|
||||
fi
|
|
@ -0,0 +1,50 @@
|
|||
# Copyright (c) HashiCorp, Inc.
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
# Full configuration options can be found at https://www.vaultproject.io/docs/configuration
|
||||
|
||||
ui = true
|
||||
|
||||
#mlock = true
|
||||
#disable_mlock = true
|
||||
|
||||
storage "file" {
|
||||
path = "/opt/vault/data"
|
||||
}
|
||||
|
||||
#storage "consul" {
|
||||
# address = "127.0.0.1:8500"
|
||||
# path = "vault"
|
||||
#}
|
||||
|
||||
# HTTP listener
|
||||
#listener "tcp" {
|
||||
# address = "127.0.0.1:8200"
|
||||
# tls_disable = 1
|
||||
#}
|
||||
|
||||
# HTTPS listener
|
||||
listener "tcp" {
|
||||
address = "0.0.0.0:8200"
|
||||
tls_cert_file = "/opt/vault/tls/tls.crt"
|
||||
tls_key_file = "/opt/vault/tls/tls.key"
|
||||
}
|
||||
|
||||
# Enterprise license_path
|
||||
# This will be required for enterprise as of v1.8
|
||||
#license_path = "/etc/vault.d/vault.hclic"
|
||||
|
||||
# Example AWS KMS auto unseal
|
||||
#seal "awskms" {
|
||||
# region = "us-east-1"
|
||||
# kms_key_id = "REPLACE-ME"
|
||||
#}
|
||||
|
||||
# Example HSM auto unseal
|
||||
#seal "pkcs11" {
|
||||
# lib = "/usr/vault/lib/libCryptoki2_64.so"
|
||||
# slot = "0"
|
||||
# pin = "AAAA-BBBB-CCCC-DDDD"
|
||||
# key_label = "vault-hsm-key"
|
||||
# hmac_key_label = "vault-hsm-hmac-key"
|
||||
#}
|
|
@ -0,0 +1,34 @@
|
|||
[Unit]
|
||||
Description="HashiCorp Vault - A tool for managing secrets"
|
||||
Documentation=https://www.vaultproject.io/docs/
|
||||
Requires=network-online.target
|
||||
After=network-online.target
|
||||
ConditionFileNotEmpty=/etc/vault.d/vault.hcl
|
||||
StartLimitIntervalSec=60
|
||||
StartLimitBurst=3
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
EnvironmentFile=/etc/vault.d/vault.env
|
||||
User=vault
|
||||
Group=vault
|
||||
ProtectSystem=full
|
||||
ProtectHome=read-only
|
||||
PrivateTmp=yes
|
||||
PrivateDevices=yes
|
||||
SecureBits=keep-caps
|
||||
AmbientCapabilities=CAP_IPC_LOCK
|
||||
CapabilityBoundingSet=CAP_SYSLOG CAP_IPC_LOCK
|
||||
NoNewPrivileges=yes
|
||||
ExecStart=/usr/bin/vault server -config=/etc/vault.d/vault.hcl
|
||||
ExecReload=/bin/kill --signal HUP $MAINPID
|
||||
KillMode=process
|
||||
KillSignal=SIGINT
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
TimeoutStopSec=30
|
||||
LimitNOFILE=65536
|
||||
LimitMEMLOCK=infinity
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -0,0 +1,47 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [[ -f /opt/vault/tls/tls.crt ]] && [[ -f /opt/vault/tls/tls.key ]]; then
|
||||
echo "Vault TLS key and certificate already exist. Exiting."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Generating Vault TLS key and self-signed certificate..."
|
||||
|
||||
# Create TLS and Data directory
|
||||
mkdir --parents /opt/vault/tls
|
||||
mkdir --parents /opt/vault/data
|
||||
|
||||
# Generate TLS key and certificate
|
||||
cd /opt/vault/tls
|
||||
openssl req \
|
||||
-out tls.crt \
|
||||
-new \
|
||||
-keyout tls.key \
|
||||
-newkey rsa:4096 \
|
||||
-nodes \
|
||||
-sha256 \
|
||||
-x509 \
|
||||
-subj "/O=HashiCorp/CN=Vault" \
|
||||
-days 1095 # 3 years
|
||||
|
||||
# Update file permissions
|
||||
chown --recursive vault:vault /etc/vault.d
|
||||
chown --recursive vault:vault /opt/vault
|
||||
chmod 600 /opt/vault/tls/tls.crt /opt/vault/tls/tls.key
|
||||
chmod 700 /opt/vault/tls
|
||||
|
||||
echo "Vault TLS key and self-signed certificate have been generated in '/opt/vault/tls'."
|
||||
|
||||
# Set IPC_LOCK capabilities on vault
|
||||
setcap cap_ipc_lock=+ep /usr/bin/vault
|
||||
|
||||
if [ -d /run/systemd/system ]; then
|
||||
systemctl --system daemon-reload >/dev/null || true
|
||||
fi
|
||||
|
||||
if [[ $(vault version) == *+ent* ]]; then
|
||||
echo "
|
||||
The following shall apply unless your organization has a separately signed Enterprise License Agreement or Evaluation Agreement governing your use of the software:
|
||||
Software in this repository is subject to the license terms located in the software, copies of which are also available at https://eula.hashicorp.com/ClickThruELA-Global.pdf or https://www.hashicorp.com/terms-of-evaluation as applicable. Please read the license terms prior to using the software. Your installation and use of the software constitutes your acceptance of these terms. If you do not accept the terms, do not use the software.
|
||||
"
|
||||
fi
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [ "$1" = "purge" ]
|
||||
then
|
||||
userdel vault
|
||||
fi
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,13 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -eu
|
||||
|
||||
USER="vault"
|
||||
|
||||
if ! id -u $USER > /dev/null 2>&1; then
|
||||
useradd \
|
||||
--system \
|
||||
--user-group \
|
||||
--shell /bin/false \
|
||||
$USER
|
||||
fi
|
|
@ -0,0 +1,9 @@
|
|||
# Copyright (c) HashiCorp, Inc.
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
url_docker_registry_dockerhub = "https://hub.docker.com/r/hashicorp/vault"
|
||||
url_docker_registry_ecr = "https://gallery.ecr.aws/hashicorp/vault"
|
||||
url_license = "https://github.com/hashicorp/vault/blob/main/LICENSE"
|
||||
url_project_website = "https://www.vaultproject.io/"
|
||||
url_source_repository = "https://github.com/hashicorp/vault"
|
||||
url_release_notes = "https://www.vaultproject.io/docs/release-notes"
|
|
@ -0,0 +1,16 @@
|
|||
# Copyright (c) HashiCorp, Inc.
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
container {
|
||||
dependencies = true
|
||||
alpine_secdb = true
|
||||
secrets = true
|
||||
}
|
||||
|
||||
binary {
|
||||
secrets = false
|
||||
go_modules = false
|
||||
osv = true
|
||||
oss_index = true
|
||||
nvd = false
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
|
||||
extends: relaxed
|
||||
|
||||
rules:
|
||||
comments: disable
|
||||
comments-indentation: disable
|
||||
line-length: disable
|
||||
commas:
|
||||
max-spaces-after: -1
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,84 @@
|
|||
# Each line is a file pattern followed by one or more owners. Being an owner
|
||||
# means those groups or individuals will be added as reviewers to PRs affecting
|
||||
# those areas of the code.
|
||||
#
|
||||
# More on CODEOWNERS files: https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners
|
||||
|
||||
# Select Auth engines are owned by Ecosystem
|
||||
/builtin/credential/aws/ @hashicorp/vault-ecosystem
|
||||
/builtin/credential/github/ @hashicorp/vault-ecosystem
|
||||
/builtin/credential/ldap/ @hashicorp/vault-ecosystem
|
||||
/builtin/credential/okta/ @hashicorp/vault-ecosystem
|
||||
|
||||
# Secrets engines (pki, ssh, totp and transit omitted)
|
||||
/builtin/logical/aws/ @hashicorp/vault-ecosystem
|
||||
/builtin/logical/cassandra/ @hashicorp/vault-ecosystem
|
||||
/builtin/logical/consul/ @hashicorp/vault-ecosystem
|
||||
/builtin/logical/database/ @hashicorp/vault-ecosystem
|
||||
/builtin/logical/mongodb/ @hashicorp/vault-ecosystem
|
||||
/builtin/logical/mssql/ @hashicorp/vault-ecosystem
|
||||
/builtin/logical/mysql/ @hashicorp/vault-ecosystem
|
||||
/builtin/logical/nomad/ @hashicorp/vault-ecosystem
|
||||
/builtin/logical/postgresql/ @hashicorp/vault-ecosystem
|
||||
/builtin/logical/rabbitmq/ @hashicorp/vault-ecosystem
|
||||
|
||||
/plugins/ @hashicorp/vault-ecosystem
|
||||
/vault/plugin_catalog.go @hashicorp/vault-ecosystem
|
||||
|
||||
/website/content/ @hashicorp/vault-education-approvers
|
||||
/website/content/docs/plugin-portal.mdx @acahn @hashicorp/vault-education-approvers
|
||||
|
||||
# Plugin docs
|
||||
/website/content/docs/plugins/ @fairclothjm @hashicorp/vault-education-approvers
|
||||
/website/content/docs/upgrading/plugins.mdx @fairclothjm @hashicorp/vault-education-approvers
|
||||
|
||||
# UI code related to Vault's JWT/OIDC auth method and OIDC provider.
|
||||
# Changes to these files often require coordination with backend code,
|
||||
# so stewards of the backend code are added below for notification.
|
||||
/ui/app/components/auth-jwt.js @austingebauer
|
||||
/ui/app/routes/vault/cluster/oidc-*.js @austingebauer
|
||||
|
||||
# Release config; service account is required for automation tooling.
|
||||
/.release/ @hashicorp/release-engineering @hashicorp/github-secure-vault-core @hashicorp/quality-team
|
||||
/.github/workflows/build.yml @hashicorp/release-engineering @hashicorp/github-secure-vault-core @hashicorp/quality-team
|
||||
|
||||
# Quality engineering
|
||||
/.github/ @hashicorp/quality-team
|
||||
/enos/ @hashicorp/quality-team
|
||||
|
||||
# Cryptosec
|
||||
/builtin/logical/pki/ @hashicorp/vault-crypto
|
||||
/builtin/logical/pkiext/ @hashicorp/vault-crypto
|
||||
/website/content/docs/secrets/pki/ @hashicorp/vault-crypto
|
||||
/website/content/api-docs/secret/pki.mdx @hashicorp/vault-crypto
|
||||
/builtin/credential/cert/ @hashicorp/vault-crypto
|
||||
/website/content/docs/auth/cert.mdx @hashicorp/vault-crypto
|
||||
/website/content/api-docs/auth/cert.mdx @hashicorp/vault-crypto
|
||||
/builtin/logical/ssh/ @hashicorp/vault-crypto
|
||||
/website/content/docs/secrets/ssh/ @hashicorp/vault-crypto
|
||||
/website/content/api-docs/secret/ssh.mdx @hashicorp/vault-crypto
|
||||
/builtin/logical/transit/ @hashicorp/vault-crypto
|
||||
/website/content/docs/secrets/transit/ @hashicorp/vault-crypto
|
||||
/website/content/api-docs/secret/transit.mdx @hashicorp/vault-crypto
|
||||
/helper/random/ @hashicorp/vault-crypto
|
||||
/sdk/helper/certutil/ @hashicorp/vault-crypto
|
||||
/sdk/helper/cryptoutil/ @hashicorp/vault-crypto
|
||||
/sdk/helper/kdf/ @hashicorp/vault-crypto
|
||||
/sdk/helper/keysutil/ @hashicorp/vault-crypto
|
||||
/sdk/helper/ocsp/ @hashicorp/vault-crypto
|
||||
/sdk/helper/salt/ @hashicorp/vault-crypto
|
||||
/sdk/helper/tlsutil/ @hashicorp/vault-crypto
|
||||
/shamir/ @hashicorp/vault-crypto
|
||||
/vault/barrier* @hashicorp/vault-crypto
|
||||
/vault/managed_key* @hashicorp/vault-crypto
|
||||
/vault/seal* @hashicorp/vault-crypto
|
||||
/vault/seal/ @hashicorp/vault-crypto
|
||||
/website/content/docs/configuration/seal/ @hashicorp/vault-crypto
|
||||
/website/content/docs/enterprise/sealwrap.mdx @hashicorp/vault-crypto
|
||||
/website/content/api-docs/system/sealwrap-rewrap.mdx @hashicorp/vault-crypto
|
||||
/website/content/docs/secrets/transform/ @hashicorp/vault-crypto
|
||||
/website/content/api-docs/secret/transform.mdx @hashicorp/vault-crypto
|
||||
/website/content/docs/secrets/kmip-profiles.mdx @hashicorp/vault-crypto
|
||||
/website/content/docs/secrets/kmip.mdx @hashicorp/vault-crypto
|
||||
/website/content/api-docs/secret/kmip.mdx @hashicorp/vault-crypto
|
||||
/website/content/docs/enterprise/fips/ @hashicorp/vault-crypto
|
|
@ -0,0 +1,146 @@
|
|||
# Contributing to Vault
|
||||
|
||||
**Please note:** We take Vault's security and our users' trust very seriously.
|
||||
If you believe you have found a security issue in Vault, please responsibly
|
||||
disclose by contacting us at security@hashicorp.com.
|
||||
|
||||
**First:** if you're unsure or afraid of _anything_, just ask or submit the
|
||||
issue or pull request anyways. You won't be yelled at for giving it your best
|
||||
effort. The worst that can happen is that you'll be politely asked to change
|
||||
something. We appreciate any sort of contributions, and don't want a wall of
|
||||
rules to get in the way of that.
|
||||
|
||||
That said, if you want to ensure that a pull request is likely to be merged,
|
||||
talk to us! You can find out our thoughts and ensure that your contribution
|
||||
won't clash or be obviated by Vault's normal direction. A great way to do this
|
||||
is via the [Vault Discussion Forum][2].
|
||||
|
||||
## Issues
|
||||
|
||||
This section will cover what we're looking for in terms of reporting issues.
|
||||
|
||||
By addressing all the points we're looking for, it raises the chances we can
|
||||
quickly merge or address your contributions.
|
||||
|
||||
### Reporting an Issue
|
||||
|
||||
* Make sure you test against the latest released version. It is possible we
|
||||
already fixed the bug you're experiencing. Even better is if you can test
|
||||
against the `main` branch, as the bugs are regularly fixed but new versions
|
||||
are only released every few months.
|
||||
|
||||
* Provide steps to reproduce the issue, and if possible include the expected
|
||||
results as well as the actual results. Please provide text, not screen shots!
|
||||
|
||||
* If you are seeing an internal Vault error (a status code of 5xx), please be
|
||||
sure to post relevant parts of (or the entire) Vault log, as often these
|
||||
errors are logged on the server but not reported to the user.
|
||||
|
||||
* If you experienced a panic, please create a [gist](https://gist.github.com)
|
||||
of the *entire* generated crash log for us to look at. Double check
|
||||
no sensitive items were in the log.
|
||||
|
||||
* Respond as promptly as possible to any questions made by the Vault
|
||||
team to your issue.
|
||||
|
||||
### Issue Lifecycle
|
||||
|
||||
1. The issue is reported.
|
||||
|
||||
2. The issue is verified and categorized by a Vault collaborator.
|
||||
Categorization is done via tags. For example, bugs are marked as "bugs".
|
||||
|
||||
3. Unless it is critical, the issue may be left for a period of time (sometimes
|
||||
many weeks), giving outside contributors -- maybe you!? -- a chance to
|
||||
address the issue.
|
||||
|
||||
4. The issue is addressed in a pull request or commit. The issue will be
|
||||
referenced in the commit message so that the code that fixes it is clearly
|
||||
linked.
|
||||
|
||||
5. The issue is closed.
|
||||
|
||||
6. Issues that are not reproducible and/or not gotten responses for a long time are
|
||||
stale issues. In order to provide faster responses and better engagement with
|
||||
the community, we strive to keep the issue tracker clean and the issue count
|
||||
low. In this regard, our current policy is to close stale issues after 30 days.
|
||||
Closed issues will still be indexed and available for future viewers. If users
|
||||
feel that the issue is still relevant, we encourage reopening them.
|
||||
|
||||
## Pull requests
|
||||
|
||||
When submitting a PR you should reference an existing issue. If no issue already exists,
|
||||
please create one. This can be skipped for trivial PRs like fixing typos.
|
||||
|
||||
Creating an issue in advance of working on the PR can help to avoid duplication of effort,
|
||||
e.g. maybe we know of existing related work. Or it may be that we can provide guidance
|
||||
that will help with your approach.
|
||||
|
||||
Your pull request should have a description of what it accomplishes, how it does so,
|
||||
and why you chose the approach you did. PRs should include unit tests that validate
|
||||
correctness and the existing tests must pass. Follow-up work to fix tests
|
||||
does not need a fresh issue filed.
|
||||
|
||||
Someone will do a first pass review on your PR making sure it follows the guidelines
|
||||
in this document. If it doesn't we'll mark the PR incomplete and ask you to follow
|
||||
up on the missing requirements.
|
||||
|
||||
### Changelog Entries
|
||||
Please include a file within your PR named `changelog/#.txt`, where `#` is your
|
||||
pull request ID. There are many examples under [changelog](changelog/), but
|
||||
the general format is
|
||||
|
||||
````
|
||||
```release-note:CATEGORY
|
||||
COMPONENT: summary of change
|
||||
```
|
||||
````
|
||||
|
||||
CATEGORY is one of `security`, `change`, `feature`, `improvement`, or `bug`.
|
||||
Your PR is almost certain to be one of `bug` or `improvement`, but don't
|
||||
worry too much about getting it exactly right, we'll tell you if a change is
|
||||
needed.
|
||||
|
||||
To determine the relevant component, consult [CHANGELOG](CHANGELOG.md) and pick
|
||||
whichever one you see that seems the closest match.
|
||||
|
||||
You do not need to include the link at the end of the summary that appears in
|
||||
CHANGELOG.md, those are generated automatically by the changelog-building
|
||||
process.
|
||||
|
||||
### Vault UI
|
||||
|
||||
How you contribute to the UI depends on what you want to contribute. If that is
|
||||
a new feature, please submit an informational issue first. That issue
|
||||
should include a short description of the proposed feature, the use case,
|
||||
the approach you're taking, and the tests that would be written. A mockup
|
||||
is optional but encouraged.
|
||||
|
||||
Bug fixes are welcome in PRs but existing tests must pass and updated logic
|
||||
should be handled in new tests. You needn't submit an issue first to fix bugs.
|
||||
|
||||
Keep in mind that the UI should be consistent with other areas of Vault.
|
||||
The UI should be user-centered, informative, and include edge cases and errors—
|
||||
including accommodations for users who may not have permissions to view or
|
||||
interact with your feature. If you are not comfortable with UI design, a Vault
|
||||
designer can take a look at your work— just be aware that this might mean
|
||||
it will add some time to the PR process.
|
||||
|
||||
Finally, in your code, try to avoid logic-heavy templates (when possible,
|
||||
calculate values in the .js file instead of .hbs) and Ember anti-patterns.
|
||||
And most of all, if you have any questions, please ask!
|
||||
|
||||
## Setting up Go to work on Vault
|
||||
|
||||
If you have never worked with Go before, you will have to complete the
|
||||
following steps listed in the README, under the section [Developing Vault][1].
|
||||
|
||||
|
||||
[1]: https://github.com/hashicorp/vault#developing-vault
|
||||
[2]: https://discuss.hashicorp.com/c/vault
|
||||
|
||||
## Contributor License Agreement
|
||||
|
||||
We require that all contributors sign our Contributor License Agreement ("CLA") before we can accept the contribution.
|
||||
|
||||
[Learn more about why HashiCorp requires a CLA and what the CLA includes](https://www.hashicorp.com/cla)
|
|
@ -0,0 +1,158 @@
|
|||
# Copyright (c) HashiCorp, Inc.
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
## DOCKERHUB DOCKERFILE ##
|
||||
FROM alpine:3.18 as default
|
||||
|
||||
ARG BIN_NAME
|
||||
# NAME and PRODUCT_VERSION are the name of the software in releases.hashicorp.com
|
||||
# and the version to download. Example: NAME=vault PRODUCT_VERSION=1.2.3.
|
||||
ARG NAME=vault
|
||||
ARG PRODUCT_VERSION
|
||||
ARG PRODUCT_REVISION
|
||||
# TARGETARCH and TARGETOS are set automatically when --platform is provided.
|
||||
ARG TARGETOS TARGETARCH
|
||||
|
||||
# Additional metadata labels used by container registries, platforms
|
||||
# and certification scanners.
|
||||
LABEL name="Vault" \
|
||||
maintainer="Vault Team <vault@hashicorp.com>" \
|
||||
vendor="HashiCorp" \
|
||||
version=${PRODUCT_VERSION} \
|
||||
release=${PRODUCT_REVISION} \
|
||||
revision=${PRODUCT_REVISION} \
|
||||
summary="Vault is a tool for securely accessing secrets." \
|
||||
description="Vault is a tool for securely accessing secrets. A secret is anything that you want to tightly control access to, such as API keys, passwords, certificates, and more. Vault provides a unified interface to any secret, while providing tight access control and recording a detailed audit log."
|
||||
|
||||
COPY LICENSE /licenses/mozilla.txt
|
||||
|
||||
# Set ARGs as ENV so that they can be used in ENTRYPOINT/CMD
|
||||
ENV NAME=$NAME
|
||||
ENV VERSION=$VERSION
|
||||
|
||||
# Create a non-root user to run the software.
|
||||
RUN addgroup ${NAME} && adduser -S -G ${NAME} ${NAME}
|
||||
|
||||
RUN apk add --no-cache libcap su-exec dumb-init tzdata
|
||||
|
||||
COPY dist/$TARGETOS/$TARGETARCH/$BIN_NAME /bin/
|
||||
|
||||
# /vault/logs is made available to use as a location to store audit logs, if
|
||||
# desired; /vault/file is made available to use as a location with the file
|
||||
# storage backend, if desired; the server will be started with /vault/config as
|
||||
# the configuration directory so you can add additional config files in that
|
||||
# location.
|
||||
RUN mkdir -p /vault/logs && \
|
||||
mkdir -p /vault/file && \
|
||||
mkdir -p /vault/config && \
|
||||
chown -R ${NAME}:${NAME} /vault
|
||||
|
||||
# Expose the logs directory as a volume since there's potentially long-running
|
||||
# state in there
|
||||
VOLUME /vault/logs
|
||||
|
||||
# Expose the file directory as a volume since there's potentially long-running
|
||||
# state in there
|
||||
VOLUME /vault/file
|
||||
|
||||
# 8200/tcp is the primary interface that applications use to interact with
|
||||
# Vault.
|
||||
EXPOSE 8200
|
||||
|
||||
# The entry point script uses dumb-init as the top-level process to reap any
|
||||
# zombie processes created by Vault sub-processes.
|
||||
#
|
||||
# For production derivatives of this container, you shoud add the IPC_LOCK
|
||||
# capability so that Vault can mlock memory.
|
||||
COPY .release/docker/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
|
||||
|
||||
# # By default you'll get a single-node development server that stores everything
|
||||
# # in RAM and bootstraps itself. Don't use this configuration for production.
|
||||
CMD ["server", "-dev"]
|
||||
|
||||
|
||||
## UBI DOCKERFILE ##
|
||||
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.8 as ubi
|
||||
|
||||
ARG BIN_NAME
|
||||
# PRODUCT_VERSION is the version built dist/$TARGETOS/$TARGETARCH/$BIN_NAME,
|
||||
# which we COPY in later. Example: PRODUCT_VERSION=1.2.3.
|
||||
ARG PRODUCT_VERSION
|
||||
ARG PRODUCT_REVISION
|
||||
# TARGETARCH and TARGETOS are set automatically when --platform is provided.
|
||||
ARG TARGETOS TARGETARCH
|
||||
|
||||
# Additional metadata labels used by container registries, platforms
|
||||
# and certification scanners.
|
||||
LABEL name="Vault" \
|
||||
maintainer="Vault Team <vault@hashicorp.com>" \
|
||||
vendor="HashiCorp" \
|
||||
version=${PRODUCT_VERSION} \
|
||||
release=${PRODUCT_REVISION} \
|
||||
revision=${PRODUCT_REVISION} \
|
||||
summary="Vault is a tool for securely accessing secrets." \
|
||||
description="Vault is a tool for securely accessing secrets. A secret is anything that you want to tightly control access to, such as API keys, passwords, certificates, and more. Vault provides a unified interface to any secret, while providing tight access control and recording a detailed audit log."
|
||||
|
||||
COPY LICENSE /licenses/mozilla.txt
|
||||
|
||||
# Set ARGs as ENV so that they can be used in ENTRYPOINT/CMD
|
||||
ENV NAME=$NAME
|
||||
ENV VERSION=$VERSION
|
||||
|
||||
# Set up certificates, our base tools, and Vault. Unlike the other version of
|
||||
# this (https://github.com/hashicorp/docker-vault/blob/master/ubi/Dockerfile),
|
||||
# we copy in the Vault binary from CRT.
|
||||
RUN set -eux; \
|
||||
microdnf install -y ca-certificates gnupg openssl libcap tzdata procps shadow-utils util-linux
|
||||
|
||||
# Create a non-root user to run the software.
|
||||
RUN groupadd --gid 1000 vault && \
|
||||
adduser --uid 100 --system -g vault vault && \
|
||||
usermod -a -G root vault
|
||||
|
||||
# Copy in the new Vault from CRT pipeline, rather than fetching it from our
|
||||
# public releases.
|
||||
COPY dist/$TARGETOS/$TARGETARCH/$BIN_NAME /bin/
|
||||
|
||||
# /vault/logs is made available to use as a location to store audit logs, if
|
||||
# desired; /vault/file is made available to use as a location with the file
|
||||
# storage backend, if desired; the server will be started with /vault/config as
|
||||
# the configuration directory so you can add additional config files in that
|
||||
# location.
|
||||
ENV HOME /home/vault
|
||||
RUN mkdir -p /vault/logs && \
|
||||
mkdir -p /vault/file && \
|
||||
mkdir -p /vault/config && \
|
||||
mkdir -p $HOME && \
|
||||
chown -R vault /vault && chown -R vault $HOME && \
|
||||
chgrp -R 0 $HOME && chmod -R g+rwX $HOME && \
|
||||
chgrp -R 0 /vault && chmod -R g+rwX /vault
|
||||
|
||||
# Expose the logs directory as a volume since there's potentially long-running
|
||||
# state in there
|
||||
VOLUME /vault/logs
|
||||
|
||||
# Expose the file directory as a volume since there's potentially long-running
|
||||
# state in there
|
||||
VOLUME /vault/file
|
||||
|
||||
# 8200/tcp is the primary interface that applications use to interact with
|
||||
# Vault.
|
||||
EXPOSE 8200
|
||||
|
||||
# The entry point script uses dumb-init as the top-level process to reap any
|
||||
# zombie processes created by Vault sub-processes.
|
||||
#
|
||||
# For production derivatives of this container, you shoud add the IPC_LOCK
|
||||
# capability so that Vault can mlock memory.
|
||||
COPY .release/docker/ubi-docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
|
||||
# Use the Vault user as the default user for starting this container.
|
||||
USER vault
|
||||
|
||||
# # By default you'll get a single-node development server that stores everything
|
||||
# # in RAM and bootstraps itself. Don't use this configuration for production.
|
||||
CMD ["server", "-dev"]
|
Binary file not shown.
After Width: | Height: | Size: 182 KiB |
|
@ -0,0 +1,365 @@
|
|||
Copyright (c) 2015 HashiCorp, Inc.
|
||||
|
||||
Mozilla Public License, version 2.0
|
||||
|
||||
1. Definitions
|
||||
|
||||
1.1. "Contributor"
|
||||
|
||||
means each individual or legal entity that creates, contributes to the
|
||||
creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
|
||||
means the combination of the Contributions of others (if any) used by a
|
||||
Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
|
||||
means Source Code Form to which the initial Contributor has attached the
|
||||
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||
Modifications of such Source Code Form, in each case including portions
|
||||
thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
a. that the initial Contributor has attached the notice described in
|
||||
Exhibit B to the Covered Software; or
|
||||
|
||||
b. that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the terms of
|
||||
a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
|
||||
means a work that combines Covered Software with other material, in a
|
||||
separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
|
||||
means having the right to grant, to the maximum extent possible, whether
|
||||
at the time of the initial grant or subsequently, any and all of the
|
||||
rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
|
||||
means any of the following:
|
||||
|
||||
a. any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered Software; or
|
||||
|
||||
b. any new file in Source Code Form that contains any Covered Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the License,
|
||||
by the making, using, selling, offering for sale, having made, import,
|
||||
or transfer of either its Contributions or its Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
|
||||
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||
General Public License, Version 2.1, the GNU Affero General Public
|
||||
License, Version 3.0, or any later versions of those licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that controls, is
|
||||
controlled by, or is under common control with You. For purposes of this
|
||||
definition, "control" means (a) the power, direct or indirect, to cause
|
||||
the direction or management of such entity, whether by contract or
|
||||
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||
outstanding shares or beneficial ownership of such entity.
|
||||
|
||||
|
||||
2. License Grants and Conditions
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
a. under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||
sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
a. for any code that a Contributor has removed from Covered Software; or
|
||||
|
||||
b. for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
c. under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights to
|
||||
grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||
Section 2.1.
|
||||
|
||||
|
||||
3. Responsibilities
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
a. such Covered Software must also be made available in Source Code Form,
|
||||
as described in Section 3.1, and You must inform recipients of the
|
||||
Executable Form how they can obtain a copy of such Source Code Form by
|
||||
reasonable means in a timely manner, at a charge no more than the cost
|
||||
of distribution to the recipient; and
|
||||
|
||||
b. You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter the
|
||||
recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty, or
|
||||
limitations of liability) contained within the Source Code Form of the
|
||||
Covered Software, except that You may alter any license notices to the
|
||||
extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this License
|
||||
with respect to some or all of the Covered Software due to statute,
|
||||
judicial order, or regulation then You must: (a) comply with the terms of
|
||||
this License to the maximum extent possible; and (b) describe the
|
||||
limitations and the code they affect. Such description must be placed in a
|
||||
text file included with all distributions of the Covered Software under
|
||||
this License. Except to the extent prohibited by statute or regulation,
|
||||
such description must be sufficiently detailed for a recipient of ordinary
|
||||
skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically if You
|
||||
fail to comply with any of its terms. However, if You become compliant,
|
||||
then the rights granted under this License from a particular Contributor
|
||||
are reinstated (a) provisionally, unless and until such Contributor
|
||||
explicitly and finally terminates Your grants, and (b) on an ongoing
|
||||
basis, if such Contributor fails to notify You of the non-compliance by
|
||||
some reasonable means prior to 60 days after You have come back into
|
||||
compliance. Moreover, Your grants from a particular Contributor are
|
||||
reinstated on an ongoing basis if such Contributor notifies You of the
|
||||
non-compliance by some reasonable means, this is the first time You have
|
||||
received notice of non-compliance with this License from such
|
||||
Contributor, and You become compliant prior to 30 days after Your receipt
|
||||
of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||
license agreements (excluding distributors and resellers) which have been
|
||||
validly granted by You or Your distributors under this License prior to
|
||||
termination shall survive termination.
|
||||
|
||||
6. Disclaimer of Warranty
|
||||
|
||||
Covered Software is provided under this License on an "as is" basis,
|
||||
without warranty of any kind, either expressed, implied, or statutory,
|
||||
including, without limitation, warranties that the Covered Software is free
|
||||
of defects, merchantable, fit for a particular purpose or non-infringing.
|
||||
The entire risk as to the quality and performance of the Covered Software
|
||||
is with You. Should any Covered Software prove defective in any respect,
|
||||
You (not any Contributor) assume the cost of any necessary servicing,
|
||||
repair, or correction. This disclaimer of warranty constitutes an essential
|
||||
part of this License. No use of any Covered Software is authorized under
|
||||
this License except under this disclaimer.
|
||||
|
||||
7. Limitation of Liability
|
||||
|
||||
Under no circumstances and under no legal theory, whether tort (including
|
||||
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||
distributes Covered Software as permitted above, be liable to You for any
|
||||
direct, indirect, special, incidental, or consequential damages of any
|
||||
character including, without limitation, damages for lost profits, loss of
|
||||
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses, even if such party shall have been
|
||||
informed of the possibility of such damages. This limitation of liability
|
||||
shall not apply to liability for death or personal injury resulting from
|
||||
such party's negligence to the extent applicable law prohibits such
|
||||
limitation. Some jurisdictions do not allow the exclusion or limitation of
|
||||
incidental or consequential damages, so this exclusion and limitation may
|
||||
not apply to You.
|
||||
|
||||
8. Litigation
|
||||
|
||||
Any litigation relating to this License may be brought only in the courts
|
||||
of a jurisdiction where the defendant maintains its principal place of
|
||||
business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions. Nothing
|
||||
in this Section shall prevent a party's ability to bring cross-claims or
|
||||
counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides that
|
||||
the language of a contract shall be construed against the drafter shall not
|
||||
be used to construe this License against a Contributor.
|
||||
|
||||
|
||||
10. Versions of the License
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses If You choose to distribute Source Code Form that is
|
||||
Incompatible With Secondary Licenses under the terms of this version of
|
||||
the License, the notice described in Exhibit B of this License must be
|
||||
attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular file,
|
||||
then You may include the notice in a location (such as a LICENSE file in a
|
||||
relevant directory) where a recipient would be likely to look for such a
|
||||
notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
|
||||
This Source Code Form is "Incompatible
|
||||
With Secondary Licenses", as defined by
|
||||
the Mozilla Public License, v. 2.0.
|
||||
|
|
@ -0,0 +1,325 @@
|
|||
# Determine this makefile's path.
|
||||
# Be sure to place this BEFORE `include` directives, if any.
|
||||
THIS_FILE := $(lastword $(MAKEFILE_LIST))
|
||||
|
||||
TEST?=$$($(GO_CMD) list ./... | grep -v /vendor/ | grep -v /integ)
|
||||
TEST_TIMEOUT?=45m
|
||||
EXTENDED_TEST_TIMEOUT=60m
|
||||
INTEG_TEST_TIMEOUT=120m
|
||||
VETARGS?=-asmdecl -atomic -bool -buildtags -copylocks -methods -nilfunc -printf -rangeloops -shift -structtags -unsafeptr
|
||||
EXTERNAL_TOOLS_CI=\
|
||||
golang.org/x/tools/cmd/goimports \
|
||||
github.com/golangci/revgrep/cmd/revgrep \
|
||||
mvdan.cc/gofumpt \
|
||||
honnef.co/go/tools/cmd/staticcheck
|
||||
EXTERNAL_TOOLS=\
|
||||
github.com/client9/misspell/cmd/misspell
|
||||
GOFMT_FILES?=$$(find . -name '*.go' | grep -v pb.go | grep -v vendor)
|
||||
SED?=$(shell command -v gsed || command -v sed)
|
||||
|
||||
GO_VERSION_MIN=$$(cat $(CURDIR)/.go-version)
|
||||
PROTOC_VERSION_MIN=3.21.12
|
||||
GO_CMD?=go
|
||||
CGO_ENABLED?=0
|
||||
ifneq ($(FDB_ENABLED), )
|
||||
CGO_ENABLED=1
|
||||
BUILD_TAGS+=foundationdb
|
||||
endif
|
||||
|
||||
default: dev
|
||||
|
||||
# bin generates the releasable binaries for Vault
|
||||
bin: prep
|
||||
@CGO_ENABLED=$(CGO_ENABLED) BUILD_TAGS='$(BUILD_TAGS) ui' sh -c "'$(CURDIR)/scripts/build.sh'"
|
||||
|
||||
# dev creates binaries for testing Vault locally. These are put
|
||||
# into ./bin/ as well as $GOPATH/bin
|
||||
dev: BUILD_TAGS+=testonly
|
||||
dev: prep
|
||||
@CGO_ENABLED=$(CGO_ENABLED) BUILD_TAGS='$(BUILD_TAGS)' VAULT_DEV_BUILD=1 sh -c "'$(CURDIR)/scripts/build.sh'"
|
||||
dev-ui: BUILD_TAGS+=testonly
|
||||
dev-ui: assetcheck prep
|
||||
@CGO_ENABLED=$(CGO_ENABLED) BUILD_TAGS='$(BUILD_TAGS) ui' VAULT_DEV_BUILD=1 sh -c "'$(CURDIR)/scripts/build.sh'"
|
||||
dev-dynamic: BUILD_TAGS+=testonly
|
||||
dev-dynamic: prep
|
||||
@CGO_ENABLED=1 BUILD_TAGS='$(BUILD_TAGS)' VAULT_DEV_BUILD=1 sh -c "'$(CURDIR)/scripts/build.sh'"
|
||||
|
||||
# *-mem variants will enable memory profiling which will write snapshots of heap usage
|
||||
# to $TMP/vaultprof every 5 minutes. These can be analyzed using `$ go tool pprof <profile_file>`.
|
||||
# Note that any build can have profiling added via: `$ BUILD_TAGS=memprofiler make ...`
|
||||
dev-mem: BUILD_TAGS+=memprofiler
|
||||
dev-mem: dev
|
||||
dev-ui-mem: BUILD_TAGS+=memprofiler
|
||||
dev-ui-mem: assetcheck dev-ui
|
||||
dev-dynamic-mem: BUILD_TAGS+=memprofiler
|
||||
dev-dynamic-mem: dev-dynamic
|
||||
|
||||
# Creates a Docker image by adding the compiled linux/amd64 binary found in ./bin.
|
||||
# The resulting image is tagged "vault:dev".
|
||||
docker-dev: BUILD_TAGS+=testonly
|
||||
docker-dev: prep
|
||||
docker build --build-arg VERSION=$(GO_VERSION_MIN) --build-arg BUILD_TAGS="$(BUILD_TAGS)" -f scripts/docker/Dockerfile -t vault:dev .
|
||||
|
||||
docker-dev-ui: BUILD_TAGS+=testonly
|
||||
docker-dev-ui: prep
|
||||
docker build --build-arg VERSION=$(GO_VERSION_MIN) --build-arg BUILD_TAGS="$(BUILD_TAGS)" -f scripts/docker/Dockerfile.ui -t vault:dev-ui .
|
||||
|
||||
# test runs the unit tests and vets the code
|
||||
test: BUILD_TAGS+=testonly
|
||||
test: prep
|
||||
@CGO_ENABLED=$(CGO_ENABLED) \
|
||||
VAULT_ADDR= \
|
||||
VAULT_TOKEN= \
|
||||
VAULT_DEV_ROOT_TOKEN_ID= \
|
||||
VAULT_ACC= \
|
||||
$(GO_CMD) test -tags='$(BUILD_TAGS)' $(TEST) $(TESTARGS) -timeout=$(TEST_TIMEOUT) -parallel=20
|
||||
|
||||
testcompile: BUILD_TAGS+=testonly
|
||||
testcompile: prep
|
||||
@for pkg in $(TEST) ; do \
|
||||
$(GO_CMD) test -v -c -tags='$(BUILD_TAGS)' $$pkg -parallel=4 ; \
|
||||
done
|
||||
|
||||
# testacc runs acceptance tests
|
||||
testacc: BUILD_TAGS+=testonly
|
||||
testacc: prep
|
||||
@if [ "$(TEST)" = "./..." ]; then \
|
||||
echo "ERROR: Set TEST to a specific package"; \
|
||||
exit 1; \
|
||||
fi
|
||||
VAULT_ACC=1 $(GO_CMD) test -tags='$(BUILD_TAGS)' $(TEST) -v $(TESTARGS) -timeout=$(EXTENDED_TEST_TIMEOUT)
|
||||
|
||||
# testrace runs the race checker
|
||||
testrace: BUILD_TAGS+=testonly
|
||||
testrace: prep
|
||||
@CGO_ENABLED=1 \
|
||||
VAULT_ADDR= \
|
||||
VAULT_TOKEN= \
|
||||
VAULT_DEV_ROOT_TOKEN_ID= \
|
||||
VAULT_ACC= \
|
||||
$(GO_CMD) test -tags='$(BUILD_TAGS)' -race $(TEST) $(TESTARGS) -timeout=$(EXTENDED_TEST_TIMEOUT) -parallel=20
|
||||
|
||||
cover:
|
||||
./scripts/coverage.sh --html
|
||||
|
||||
# vet runs the Go source code static analysis tool `vet` to find
|
||||
# any common errors.
|
||||
vet:
|
||||
@$(GO_CMD) list -f '{{.Dir}}' ./... | grep -v /vendor/ \
|
||||
| grep -v '.*github.com/hashicorp/vault$$' \
|
||||
| xargs $(GO_CMD) vet ; if [ $$? -eq 1 ]; then \
|
||||
echo ""; \
|
||||
echo "Vet found suspicious constructs. Please check the reported constructs"; \
|
||||
echo "and fix them if necessary before submitting the code for reviewal."; \
|
||||
fi
|
||||
|
||||
# deprecations runs staticcheck tool to look for deprecations. Checks entire code to see if it
|
||||
# has deprecated function, variable, constant or field
|
||||
deprecations: bootstrap prep
|
||||
@BUILD_TAGS='$(BUILD_TAGS)' ./scripts/deprecations-checker.sh ""
|
||||
|
||||
# ci-deprecations runs staticcheck tool to look for deprecations. All output gets piped to revgrep
|
||||
# which will only return an error if changes that is not on main has deprecated function, variable, constant or field
|
||||
ci-deprecations: ci-bootstrap prep
|
||||
@BUILD_TAGS='$(BUILD_TAGS)' ./scripts/deprecations-checker.sh main
|
||||
|
||||
tools/codechecker/.bin/codechecker:
|
||||
@cd tools/codechecker && $(GO_CMD) build -o .bin/codechecker .
|
||||
|
||||
# vet-codechecker runs our custom linters on the test functions. All output gets
|
||||
# piped to revgrep which will only return an error if new piece of code violates
|
||||
# the check
|
||||
vet-codechecker: bootstrap tools/codechecker/.bin/codechecker prep
|
||||
@$(GO_CMD) vet -vettool=./tools/codechecker/.bin/codechecker -tags=$(BUILD_TAGS) ./... 2>&1 | revgrep
|
||||
|
||||
# vet-codechecker runs our custom linters on the test functions. All output gets
|
||||
# piped to revgrep which will only return an error if new piece of code that is
|
||||
# not on main violates the check
|
||||
ci-vet-codechecker: ci-bootstrap tools/codechecker/.bin/codechecker prep
|
||||
@$(GO_CMD) vet -vettool=./tools/codechecker/.bin/codechecker -tags=$(BUILD_TAGS) ./... 2>&1 | revgrep origin/main
|
||||
|
||||
# lint runs vet plus a number of other checkers, it is more comprehensive, but louder
|
||||
lint:
|
||||
@$(GO_CMD) list -f '{{.Dir}}' ./... | grep -v /vendor/ \
|
||||
| xargs golangci-lint run; if [ $$? -eq 1 ]; then \
|
||||
echo ""; \
|
||||
echo "Lint found suspicious constructs. Please check the reported constructs"; \
|
||||
echo "and fix them if necessary before submitting the code for reviewal."; \
|
||||
fi
|
||||
# for ci jobs, runs lint against the changed packages in the commit
|
||||
ci-lint:
|
||||
@golangci-lint run --deadline 10m --new-from-rev=HEAD~
|
||||
|
||||
# prep runs `go generate` to build the dynamically generated
|
||||
# source files.
|
||||
#
|
||||
# n.b.: prep used to depend on fmtcheck, but since fmtcheck is
|
||||
# now run as a pre-commit hook (and there's little value in
|
||||
# making every build run the formatter), we've removed that
|
||||
# dependency.
|
||||
prep:
|
||||
@sh -c "'$(CURDIR)/scripts/goversioncheck.sh' '$(GO_VERSION_MIN)'"
|
||||
@GOARCH= GOOS= $(GO_CMD) generate $$($(GO_CMD) list ./... | grep -v /vendor/)
|
||||
@if [ -d .git/hooks ]; then cp .hooks/* .git/hooks/; fi
|
||||
|
||||
# bootstrap the build by downloading additional tools needed to build
|
||||
ci-bootstrap: .ci-bootstrap
|
||||
.ci-bootstrap:
|
||||
@for tool in $(EXTERNAL_TOOLS_CI) ; do \
|
||||
echo "Installing/Updating $$tool" ; \
|
||||
GO111MODULE=off $(GO_CMD) get -u $$tool; \
|
||||
done
|
||||
@touch .ci-bootstrap
|
||||
|
||||
# bootstrap the build by downloading additional tools that may be used by devs
|
||||
bootstrap: ci-bootstrap
|
||||
go generate -tags tools tools/tools.go
|
||||
|
||||
# Note: if you have plugins in GOPATH you can update all of them via something like:
|
||||
# for i in $(ls | grep vault-plugin-); do cd $i; git remote update; git reset --hard origin/master; dep ensure -update; git add .; git commit; git push; cd ..; done
|
||||
update-plugins:
|
||||
grep vault-plugin- go.mod | cut -d ' ' -f 1 | while read -r P; do echo "Updating $P..."; go get -v "$P"; done
|
||||
|
||||
static-assets-dir:
|
||||
@mkdir -p ./http/web_ui
|
||||
|
||||
install-ui-dependencies:
|
||||
@echo "--> Installing JavaScript assets"
|
||||
@cd ui && yarn
|
||||
|
||||
test-ember: install-ui-dependencies
|
||||
@echo "--> Running ember tests"
|
||||
@cd ui && yarn run test:oss
|
||||
|
||||
test-ember-enos: install-ui-dependencies
|
||||
@echo "--> Running ember tests with a real backend"
|
||||
@cd ui && yarn run test:enos
|
||||
|
||||
check-vault-in-path:
|
||||
@VAULT_BIN=$$(command -v vault) || { echo "vault command not found"; exit 1; }; \
|
||||
[ -x "$$VAULT_BIN" ] || { echo "$$VAULT_BIN not executable"; exit 1; }; \
|
||||
printf "Using Vault at %s:\n\$$ vault version\n%s\n" "$$VAULT_BIN" "$$(vault version)"
|
||||
|
||||
ember-dist: install-ui-dependencies
|
||||
@cd ui && npm rebuild node-sass
|
||||
@echo "--> Building Ember application"
|
||||
@cd ui && yarn run build
|
||||
@rm -rf ui/if-you-need-to-delete-this-open-an-issue-async-disk-cache
|
||||
|
||||
ember-dist-dev: install-ui-dependencies
|
||||
@cd ui && npm rebuild node-sass
|
||||
@echo "--> Building Ember application"
|
||||
@cd ui && yarn run build:dev
|
||||
|
||||
static-dist: ember-dist
|
||||
static-dist-dev: ember-dist-dev
|
||||
|
||||
proto: bootstrap
|
||||
@sh -c "'$(CURDIR)/scripts/protocversioncheck.sh' '$(PROTOC_VERSION_MIN)'"
|
||||
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative vault/*.proto
|
||||
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative vault/activity/activity_log.proto
|
||||
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative vault/activity/generation/generate_data.proto
|
||||
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative helper/storagepacker/types.proto
|
||||
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative helper/forwarding/types.proto
|
||||
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative sdk/logical/*.proto
|
||||
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative physical/raft/types.proto
|
||||
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative helper/identity/mfa/types.proto
|
||||
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative helper/identity/types.proto
|
||||
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative sdk/database/dbplugin/*.proto
|
||||
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative sdk/database/dbplugin/v5/proto/*.proto
|
||||
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative sdk/plugin/pb/*.proto
|
||||
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative vault/tokens/token.proto
|
||||
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative sdk/helper/pluginutil/*.proto
|
||||
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative vault/hcp_link/proto/*/*.proto
|
||||
|
||||
# No additional sed expressions should be added to this list. Going forward
|
||||
# we should just use the variable names choosen by protobuf. These are left
|
||||
# here for backwards compatability, namely for SDK compilation.
|
||||
$(SED) -i -e 's/Id/ID/' -e 's/SPDX-License-IDentifier/SPDX-License-Identifier/' vault/request_forwarding_service.pb.go
|
||||
$(SED) -i -e 's/Idp/IDP/' -e 's/Url/URL/' -e 's/Id/ID/' -e 's/IDentity/Identity/' -e 's/EntityId/EntityID/' -e 's/Api/API/' -e 's/Qr/QR/' -e 's/Totp/TOTP/' -e 's/Mfa/MFA/' -e 's/Pingid/PingID/' -e 's/namespaceId/namespaceID/' -e 's/Ttl/TTL/' -e 's/BoundCidrs/BoundCIDRs/' -e 's/SPDX-License-IDentifier/SPDX-License-Identifier/' helper/identity/types.pb.go helper/identity/mfa/types.pb.go helper/storagepacker/types.pb.go sdk/plugin/pb/backend.pb.go sdk/logical/identity.pb.go vault/activity/activity_log.pb.go
|
||||
|
||||
# This will inject the sentinel struct tags as decorated in the proto files.
|
||||
protoc-go-inject-tag -input=./helper/identity/types.pb.go
|
||||
protoc-go-inject-tag -input=./helper/identity/mfa/types.pb.go
|
||||
|
||||
fmtcheck:
|
||||
@sh -c "'$(CURDIR)/scripts/gofmtcheck.sh'"
|
||||
|
||||
fmt: ci-bootstrap
|
||||
find . -name '*.go' | grep -v pb.go | grep -v vendor | xargs go run mvdan.cc/gofumpt -w
|
||||
|
||||
semgrep:
|
||||
semgrep --include '*.go' --exclude 'vendor' -a -f tools/semgrep .
|
||||
|
||||
semgrep-ci:
|
||||
semgrep --error --include '*.go' --exclude 'vendor' -f tools/semgrep/ci .
|
||||
|
||||
assetcheck:
|
||||
@echo "==> Checking compiled UI assets..."
|
||||
@sh -c "'$(CURDIR)/scripts/assetcheck.sh'"
|
||||
|
||||
spellcheck:
|
||||
@echo "==> Spell checking website..."
|
||||
@misspell -error -source=text website/source
|
||||
|
||||
mysql-database-plugin:
|
||||
@CGO_ENABLED=0 $(GO_CMD) build -o bin/mysql-database-plugin ./plugins/database/mysql/mysql-database-plugin
|
||||
|
||||
mysql-legacy-database-plugin:
|
||||
@CGO_ENABLED=0 $(GO_CMD) build -o bin/mysql-legacy-database-plugin ./plugins/database/mysql/mysql-legacy-database-plugin
|
||||
|
||||
cassandra-database-plugin:
|
||||
@CGO_ENABLED=0 $(GO_CMD) build -o bin/cassandra-database-plugin ./plugins/database/cassandra/cassandra-database-plugin
|
||||
|
||||
influxdb-database-plugin:
|
||||
@CGO_ENABLED=0 $(GO_CMD) build -o bin/influxdb-database-plugin ./plugins/database/influxdb/influxdb-database-plugin
|
||||
|
||||
postgresql-database-plugin:
|
||||
@CGO_ENABLED=0 $(GO_CMD) build -o bin/postgresql-database-plugin ./plugins/database/postgresql/postgresql-database-plugin
|
||||
|
||||
mssql-database-plugin:
|
||||
@CGO_ENABLED=0 $(GO_CMD) build -o bin/mssql-database-plugin ./plugins/database/mssql/mssql-database-plugin
|
||||
|
||||
hana-database-plugin:
|
||||
@CGO_ENABLED=0 $(GO_CMD) build -o bin/hana-database-plugin ./plugins/database/hana/hana-database-plugin
|
||||
|
||||
mongodb-database-plugin:
|
||||
@CGO_ENABLED=0 $(GO_CMD) build -o bin/mongodb-database-plugin ./plugins/database/mongodb/mongodb-database-plugin
|
||||
|
||||
.PHONY: bin default prep test vet bootstrap ci-bootstrap fmt fmtcheck mysql-database-plugin mysql-legacy-database-plugin cassandra-database-plugin influxdb-database-plugin postgresql-database-plugin mssql-database-plugin hana-database-plugin mongodb-database-plugin ember-dist ember-dist-dev static-dist static-dist-dev assetcheck check-vault-in-path packages build build-ci semgrep semgrep-ci vet-godoctests ci-vet-godoctests
|
||||
|
||||
.NOTPARALLEL: ember-dist ember-dist-dev
|
||||
|
||||
# These ci targets are used for used for building and testing in Github Actions
|
||||
# workflows and for Enos scenarios.
|
||||
.PHONY: ci-build
|
||||
ci-build:
|
||||
@$(CURDIR)/scripts/ci-helper.sh build
|
||||
|
||||
.PHONY: ci-build-ui
|
||||
ci-build-ui:
|
||||
@$(CURDIR)/scripts/ci-helper.sh build-ui
|
||||
|
||||
.PHONY: ci-bundle
|
||||
ci-bundle:
|
||||
@$(CURDIR)/scripts/ci-helper.sh bundle
|
||||
|
||||
.PHONY: ci-get-artifact-basename
|
||||
ci-get-artifact-basename:
|
||||
@$(CURDIR)/scripts/ci-helper.sh artifact-basename
|
||||
|
||||
.PHONY: ci-get-date
|
||||
ci-get-date:
|
||||
@$(CURDIR)/scripts/ci-helper.sh date
|
||||
|
||||
.PHONY: ci-get-revision
|
||||
ci-get-revision:
|
||||
@$(CURDIR)/scripts/ci-helper.sh revision
|
||||
|
||||
.PHONY: ci-get-version-package
|
||||
ci-get-version-package:
|
||||
@$(CURDIR)/scripts/ci-helper.sh version-package
|
||||
|
||||
.PHONY: ci-prepare-legal
|
||||
ci-prepare-legal:
|
||||
@$(CURDIR)/scripts/ci-helper.sh prepare-legal
|
|
@ -0,0 +1,288 @@
|
|||
# Vault [![CircleCI](https://circleci.com/gh/hashicorp/vault.svg?style=svg)](https://circleci.com/gh/hashicorp/vault) [![vault enterprise](https://img.shields.io/badge/vault-enterprise-yellow.svg?colorB=7c8797&colorA=000000)](https://www.hashicorp.com/products/vault/?utm_source=github&utm_medium=banner&utm_campaign=github-vault-enterprise)
|
||||
|
||||
----
|
||||
|
||||
**Please note**: We take Vault's security and our users' trust very seriously. If you believe you have found a security issue in Vault, _please responsibly disclose_ by contacting us at [security@hashicorp.com](mailto:security@hashicorp.com).
|
||||
|
||||
----
|
||||
|
||||
- Website: https://www.vaultproject.io
|
||||
- Announcement list: [Google Groups](https://groups.google.com/group/hashicorp-announce)
|
||||
- Discussion forum: [Discuss](https://discuss.hashicorp.com/c/vault)
|
||||
- Documentation: [https://www.vaultproject.io/docs/](https://www.vaultproject.io/docs/)
|
||||
- Tutorials: [HashiCorp's Learn Platform](https://learn.hashicorp.com/vault)
|
||||
- Certification Exam: [Vault Associate](https://www.hashicorp.com/certification/#hashicorp-certified-vault-associate)
|
||||
|
||||
<img width="300" alt="Vault Logo" src="https://github.com/hashicorp/vault/blob/f22d202cde2018f9455dec755118a9b84586e082/Vault_PrimaryLogo_Black.png">
|
||||
|
||||
Vault is a tool for securely accessing secrets. A secret is anything that you want to tightly control access to, such as API keys, passwords, certificates, and more. Vault provides a unified interface to any secret, while providing tight access control and recording a detailed audit log.
|
||||
|
||||
A modern system requires access to a multitude of secrets: database credentials, API keys for external services, credentials for service-oriented architecture communication, etc. Understanding who is accessing what secrets is already very difficult and platform-specific. Adding on key rolling, secure storage, and detailed audit logs is almost impossible without a custom solution. This is where Vault steps in.
|
||||
|
||||
The key features of Vault are:
|
||||
|
||||
* **Secure Secret Storage**: Arbitrary key/value secrets can be stored
|
||||
in Vault. Vault encrypts these secrets prior to writing them to persistent
|
||||
storage, so gaining access to the raw storage isn't enough to access
|
||||
your secrets. Vault can write to disk, [Consul](https://www.consul.io),
|
||||
and more.
|
||||
|
||||
* **Dynamic Secrets**: Vault can generate secrets on-demand for some
|
||||
systems, such as AWS or SQL databases. For example, when an application
|
||||
needs to access an S3 bucket, it asks Vault for credentials, and Vault
|
||||
will generate an AWS keypair with valid permissions on demand. After
|
||||
creating these dynamic secrets, Vault will also automatically revoke them
|
||||
after the lease is up.
|
||||
|
||||
* **Data Encryption**: Vault can encrypt and decrypt data without storing
|
||||
it. This allows security teams to define encryption parameters and
|
||||
developers to store encrypted data in a location such as a SQL database without
|
||||
having to design their own encryption methods.
|
||||
|
||||
* **Leasing and Renewal**: All secrets in Vault have a _lease_ associated
|
||||
with them. At the end of the lease, Vault will automatically revoke that
|
||||
secret. Clients are able to renew leases via built-in renew APIs.
|
||||
|
||||
* **Revocation**: Vault has built-in support for secret revocation. Vault
|
||||
can revoke not only single secrets, but a tree of secrets, for example,
|
||||
all secrets read by a specific user, or all secrets of a particular type.
|
||||
Revocation assists in key rolling as well as locking down systems in the
|
||||
case of an intrusion.
|
||||
|
||||
Documentation, Getting Started, and Certification Exams
|
||||
-------------------------------
|
||||
|
||||
Documentation is available on the [Vault website](https://www.vaultproject.io/docs/).
|
||||
|
||||
If you're new to Vault and want to get started with security automation, please
|
||||
check out our [Getting Started guides](https://learn.hashicorp.com/collections/vault/getting-started)
|
||||
on HashiCorp's learning platform. There are also [additional guides](https://learn.hashicorp.com/vault)
|
||||
to continue your learning.
|
||||
|
||||
For examples of how to interact with Vault from inside your application in different programming languages, see the [vault-examples](https://github.com/hashicorp/vault-examples) repo. An out-of-the-box [sample application](https://github.com/hashicorp/hello-vault-go) is also available.
|
||||
|
||||
Show off your Vault knowledge by passing a certification exam. Visit the
|
||||
[certification page](https://www.hashicorp.com/certification/#hashicorp-certified-vault-associate)
|
||||
for information about exams and find [study materials](https://learn.hashicorp.com/collections/vault/certification)
|
||||
on HashiCorp's learning platform.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
your path as some distributions bundle the old version of build tools. Next, clone this
|
||||
repository. Vault uses [Go Modules](https://github.com/golang/go/wiki/Modules),
|
||||
so it is recommended that you clone the repository ***outside*** of the GOPATH.
|
||||
You can then download any required build tools by bootstrapping your environment:
|
||||
|
||||
```sh
|
||||
$ make bootstrap
|
||||
...
|
||||
```
|
||||
|
||||
To compile a development version of Vault, run `make` or `make dev`. This will
|
||||
put the Vault binary in the `bin` and `$GOPATH/bin` folders:
|
||||
|
||||
```sh
|
||||
$ make dev
|
||||
...
|
||||
$ bin/vault
|
||||
...
|
||||
```
|
||||
|
||||
To compile a development version of Vault with the UI, run `make static-dist dev-ui`. This will
|
||||
put the Vault binary in the `bin` and `$GOPATH/bin` folders:
|
||||
|
||||
```sh
|
||||
$ make static-dist dev-ui
|
||||
...
|
||||
$ bin/vault
|
||||
...
|
||||
```
|
||||
|
||||
To run tests, type `make test`. Note: this requires Docker to be installed. If
|
||||
this exits with exit status 0, then everything is working!
|
||||
|
||||
```sh
|
||||
$ make test
|
||||
...
|
||||
```
|
||||
|
||||
If you're developing a specific package, you can run tests for just that
|
||||
package by specifying the `TEST` variable. For example below, only
|
||||
`vault` package tests will be run.
|
||||
|
||||
```sh
|
||||
$ make test TEST=./vault
|
||||
...
|
||||
```
|
||||
|
||||
### Importing Vault
|
||||
|
||||
This repository publishes two libraries that may be imported by other projects:
|
||||
`github.com/hashicorp/vault/api` and `github.com/hashicorp/vault/sdk`.
|
||||
|
||||
Note that this repository also contains Vault (the product), and as with most Go
|
||||
projects, Vault uses Go modules to manage its dependencies. The mechanism to do
|
||||
that is the [go.mod](./go.mod) file. As it happens, the presence of that file
|
||||
also makes it theoretically possible to import Vault as a dependency into other
|
||||
projects. Some other projects have made a practice of doing so in order to take
|
||||
advantage of testing tooling that was developed for testing Vault itself. This
|
||||
is not, and has never been, a supported way to use the Vault project. We aren't
|
||||
likely to fix bugs relating to failure to import `github.com/hashicorp/vault`
|
||||
into your project.
|
||||
|
||||
See also the section "Docker-based tests" below.
|
||||
|
||||
### Acceptance Tests
|
||||
|
||||
Vault has comprehensive [acceptance tests](https://en.wikipedia.org/wiki/Acceptance_testing)
|
||||
covering most of the features of the secret and auth methods.
|
||||
|
||||
If you're working on a feature of a secret or auth method and want to
|
||||
verify it is functioning (and also hasn't broken anything else), we recommend
|
||||
running the acceptance tests.
|
||||
|
||||
**Warning:** The acceptance tests create/destroy/modify *real resources*, which
|
||||
may incur real costs in some cases. In the presence of a bug, it is technically
|
||||
possible that broken backends could leave dangling data behind. Therefore,
|
||||
please run the acceptance tests at your own risk. At the very least,
|
||||
we recommend running them in their own private account for whatever backend
|
||||
you're testing.
|
||||
|
||||
To run the acceptance tests, invoke `make testacc`:
|
||||
|
||||
```sh
|
||||
$ make testacc TEST=./builtin/logical/consul
|
||||
...
|
||||
```
|
||||
|
||||
The `TEST` variable is required, and you should specify the folder where the
|
||||
backend is. The `TESTARGS` variable is recommended to filter down to a specific
|
||||
resource to test, since testing all of them at once can sometimes take a very
|
||||
long time.
|
||||
|
||||
Acceptance tests typically require other environment variables to be set for
|
||||
things such as access keys. The test itself should error early and tell
|
||||
you what to set, so it is not documented here.
|
||||
|
||||
For more information on Vault Enterprise features, visit the [Vault Enterprise site](https://www.hashicorp.com/products/vault/?utm_source=github&utm_medium=referral&utm_campaign=github-vault-enterprise).
|
||||
|
||||
### Docker-based Tests
|
||||
|
||||
We have created an experimental new testing mechanism inspired by NewTestCluster.
|
||||
An example of how to use it:
|
||||
|
||||
```go
|
||||
import (
|
||||
"testing"
|
||||
"github.com/hashicorp/vault/sdk/helper/testcluster/docker"
|
||||
)
|
||||
|
||||
func Test_Something_With_Docker(t *testing.T) {
|
||||
opts := &docker.DockerClusterOptions{
|
||||
ImageRepo: "hashicorp/vault", // or "hashicorp/vault-enterprise"
|
||||
ImageTag: "latest",
|
||||
}
|
||||
cluster := docker.NewTestDockerCluster(t, opts)
|
||||
defer cluster.Cleanup()
|
||||
|
||||
client := cluster.Nodes()[0].APIClient()
|
||||
_, err := client.Logical().Read("sys/storage/raft/configuration")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Or for Enterprise:
|
||||
|
||||
```go
|
||||
import (
|
||||
"testing"
|
||||
"github.com/hashicorp/vault/sdk/helper/testcluster/docker"
|
||||
)
|
||||
|
||||
func Test_Something_With_Docker(t *testing.T) {
|
||||
opts := &docker.DockerClusterOptions{
|
||||
ImageRepo: "hashicorp/vault-enterprise",
|
||||
ImageTag: "latest",
|
||||
VaultLicense: licenseString, // not a path, the actual license bytes
|
||||
}
|
||||
cluster := docker.NewTestDockerCluster(t, opts)
|
||||
defer cluster.Cleanup()
|
||||
}
|
||||
```
|
||||
|
||||
Here is a more realistic example of how we use it in practice. DefaultOptions uses
|
||||
`hashicorp/vault`:`latest` as the repo and tag, but it also looks at the environment
|
||||
variable VAULT_BINARY. If populated, it will copy the local file referenced by
|
||||
VAULT_BINARY into the container. This is useful when testing local changes.
|
||||
|
||||
Instead of setting the VaultLicense option, you can set the VAULT_LICENSE_CI environment
|
||||
variable, which is better than committing a license to version control.
|
||||
|
||||
Optionally you can set COMMIT_SHA, which will be appended to the image name we
|
||||
build as a debugging convenience.
|
||||
|
||||
```go
|
||||
func Test_Custom_Build_With_Docker(t *testing.T) {
|
||||
opts := docker.DefaultOptions(t)
|
||||
cluster := docker.NewTestDockerCluster(t, opts)
|
||||
defer cluster.Cleanup()
|
||||
}
|
||||
```
|
||||
|
||||
There are a variety of helpers in the `github.com/hashicorp/vault/sdk/helper/testcluster`
|
||||
package, e.g. these tests below will create a pair of 3-node clusters and link them using
|
||||
PR or DR replication respectively, and fail if the replication state doesn't become healthy
|
||||
before the passed context expires.
|
||||
|
||||
Again, as written, these depend on having a Vault Enterprise binary locally and the env
|
||||
var VAULT_BINARY set to point to it, as well as having VAULT_LICENSE_CI set.
|
||||
|
||||
```go
|
||||
func TestStandardPerfReplication_Docker(t *testing.T) {
|
||||
opts := docker.DefaultOptions(t)
|
||||
r, err := docker.NewReplicationSetDocker(t, opts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer r.Cleanup()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
err = r.StandardPerfReplication(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStandardDRReplication_Docker(t *testing.T) {
|
||||
opts := docker.DefaultOptions(t)
|
||||
r, err := docker.NewReplicationSetDocker(t, opts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer r.Cleanup()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
err = r.StandardDRReplication(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Finally, here's an example of running an existing OSS docker test with a custom binary:
|
||||
|
||||
```bash
|
||||
$ GOOS=linux make dev
|
||||
$ VAULT_BINARY=$(pwd)/bin/vault go test -run 'TestRaft_Configuration_Docker' ./vault/external_tests/raft/raft_binary
|
||||
ok github.com/hashicorp/vault/vault/external_tests/raft/raft_binary 20.960s
|
||||
```
|
|
@ -0,0 +1,9 @@
|
|||
Vault API
|
||||
=================
|
||||
|
||||
This provides the `github.com/hashicorp/vault/api` package which contains code useful for interacting with a Vault server.
|
||||
|
||||
For examples of how to use this module, see the [vault-examples](https://github.com/hashicorp/vault-examples) repo.
|
||||
For a step-by-step walkthrough on using these client libraries, see the [developer quickstart](https://www.vaultproject.io/docs/get-started/developer-qs).
|
||||
|
||||
[![GoDoc](https://godoc.org/github.com/hashicorp/vault/api?status.png)](https://godoc.org/github.com/hashicorp/vault/api)
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// testHTTPServer creates a test HTTP server that handles requests until
|
||||
// the listener returned is closed.
|
||||
func testHTTPServer(t *testing.T, handler http.Handler) (*Config, net.Listener) {
|
||||
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
server := &http.Server{Handler: handler}
|
||||
go server.Serve(ln)
|
||||
|
||||
config := DefaultConfig()
|
||||
config.Address = fmt.Sprintf("http://%s", ln.Addr())
|
||||
|
||||
return config, ln
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Auth is used to perform credential backend related operations.
|
||||
type Auth struct {
|
||||
c *Client
|
||||
}
|
||||
|
||||
type AuthMethod interface {
|
||||
Login(ctx context.Context, client *Client) (*Secret, error)
|
||||
}
|
||||
|
||||
// Auth is used to return the client for credential-backend API calls.
|
||||
func (c *Client) Auth() *Auth {
|
||||
return &Auth{c: c}
|
||||
}
|
||||
|
||||
// Login sets up the required request body for login requests to the given auth
|
||||
// method's /login API endpoint, and then performs a write to it. After a
|
||||
// successful login, this method will automatically set the client's token to
|
||||
// the login response's ClientToken as well.
|
||||
//
|
||||
// The Secret returned is the authentication secret, which if desired can be
|
||||
// passed as input to the NewLifetimeWatcher method in order to start
|
||||
// automatically renewing the token.
|
||||
func (a *Auth) Login(ctx context.Context, authMethod AuthMethod) (*Secret, error) {
|
||||
if authMethod == nil {
|
||||
return nil, fmt.Errorf("no auth method provided for login")
|
||||
}
|
||||
return a.login(ctx, authMethod)
|
||||
}
|
||||
|
||||
// MFALogin is a wrapper that helps satisfy Vault's MFA implementation.
|
||||
// If optional credentials are provided a single-phase login will be attempted
|
||||
// and the resulting Secret will contain a ClientToken if the authentication is successful.
|
||||
// The client's token will also be set accordingly.
|
||||
//
|
||||
// If no credentials are provided a two-phase MFA login will be assumed and the resulting
|
||||
// Secret will have a MFARequirement containing the MFARequestID to be used in a follow-up
|
||||
// call to `sys/mfa/validate` or by passing it to the method (*Auth).MFAValidate.
|
||||
func (a *Auth) MFALogin(ctx context.Context, authMethod AuthMethod, creds ...string) (*Secret, error) {
|
||||
if len(creds) > 0 {
|
||||
a.c.SetMFACreds(creds)
|
||||
return a.login(ctx, authMethod)
|
||||
}
|
||||
|
||||
return a.twoPhaseMFALogin(ctx, authMethod)
|
||||
}
|
||||
|
||||
// MFAValidate validates an MFA request using the appropriate payload and a secret containing
|
||||
// Auth.MFARequirement, like the one returned by MFALogin when credentials are not provided.
|
||||
// Upon successful validation the client token will be set accordingly.
|
||||
//
|
||||
// The Secret returned is the authentication secret, which if desired can be
|
||||
// passed as input to the NewLifetimeWatcher method in order to start
|
||||
// automatically renewing the token.
|
||||
func (a *Auth) MFAValidate(ctx context.Context, mfaSecret *Secret, payload map[string]interface{}) (*Secret, error) {
|
||||
if mfaSecret == nil || mfaSecret.Auth == nil || mfaSecret.Auth.MFARequirement == nil {
|
||||
return nil, fmt.Errorf("secret does not contain MFARequirements")
|
||||
}
|
||||
|
||||
s, err := a.c.Sys().MFAValidateWithContext(ctx, mfaSecret.Auth.MFARequirement.MFARequestID, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return a.checkAndSetToken(s)
|
||||
}
|
||||
|
||||
// login performs the (*AuthMethod).Login() with the configured client and checks that a ClientToken is returned
|
||||
func (a *Auth) login(ctx context.Context, authMethod AuthMethod) (*Secret, error) {
|
||||
s, err := authMethod.Login(ctx, a.c)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to log in to auth method: %w", err)
|
||||
}
|
||||
|
||||
return a.checkAndSetToken(s)
|
||||
}
|
||||
|
||||
// twoPhaseMFALogin performs the (*AuthMethod).Login() with the configured client
|
||||
// and checks that an MFARequirement is returned
|
||||
func (a *Auth) twoPhaseMFALogin(ctx context.Context, authMethod AuthMethod) (*Secret, error) {
|
||||
s, err := authMethod.Login(ctx, a.c)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to log in: %w", err)
|
||||
}
|
||||
if s == nil || s.Auth == nil || s.Auth.MFARequirement == nil {
|
||||
if s != nil {
|
||||
s.Warnings = append(s.Warnings, "expected secret to contain MFARequirements")
|
||||
}
|
||||
return s, fmt.Errorf("assumed two-phase MFA login, returned secret is missing MFARequirements")
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (a *Auth) checkAndSetToken(s *Secret) (*Secret, error) {
|
||||
if s == nil || s.Auth == nil || s.Auth.ClientToken == "" {
|
||||
if s != nil {
|
||||
s.Warnings = append(s.Warnings, "expected secret to contain ClientToken")
|
||||
}
|
||||
return s, fmt.Errorf("response did not return ClientToken, client token not set")
|
||||
}
|
||||
|
||||
a.c.SetToken(s.Auth.ClientToken)
|
||||
|
||||
return s, nil
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package approle
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/vault/api"
|
||||
)
|
||||
|
||||
type AppRoleAuth struct {
|
||||
mountPath string
|
||||
roleID string
|
||||
secretID string
|
||||
secretIDFile string
|
||||
secretIDEnv string
|
||||
unwrap bool
|
||||
}
|
||||
|
||||
var _ api.AuthMethod = (*AppRoleAuth)(nil)
|
||||
|
||||
// SecretID is a struct that allows you to specify where your application is
|
||||
// storing the secret ID required for login to the AppRole auth method. The
|
||||
// recommended secure pattern is to use response-wrapping tokens rather than
|
||||
// a plaintext value, by passing WithWrappingToken() to NewAppRoleAuth.
|
||||
// https://learn.hashicorp.com/tutorials/vault/approle-best-practices?in=vault/auth-methods#secretid-delivery-best-practices
|
||||
type SecretID struct {
|
||||
// Path on the file system where the secret ID can be found.
|
||||
FromFile string
|
||||
// The name of the environment variable containing the application's
|
||||
// secret ID.
|
||||
FromEnv string
|
||||
// The secret ID as a plaintext string value.
|
||||
FromString string
|
||||
}
|
||||
|
||||
type LoginOption func(a *AppRoleAuth) error
|
||||
|
||||
const (
|
||||
defaultMountPath = "approle"
|
||||
)
|
||||
|
||||
// NewAppRoleAuth initializes a new AppRole auth method interface to be
|
||||
// passed as a parameter to the client.Auth().Login method.
|
||||
//
|
||||
// For a secret ID, the recommended secure pattern is to unwrap a one-time-use
|
||||
// response-wrapping token that was placed here by a trusted orchestrator
|
||||
// (https://learn.hashicorp.com/tutorials/vault/approle-best-practices?in=vault/auth-methods#secretid-delivery-best-practices)
|
||||
// To indicate that the filepath points to this wrapping token and not just
|
||||
// a plaintext secret ID, initialize NewAppRoleAuth with the
|
||||
// WithWrappingToken LoginOption.
|
||||
//
|
||||
// Supported options: WithMountPath, WithWrappingToken
|
||||
func NewAppRoleAuth(roleID string, secretID *SecretID, opts ...LoginOption) (*AppRoleAuth, error) {
|
||||
if roleID == "" {
|
||||
return nil, fmt.Errorf("no role ID provided for login")
|
||||
}
|
||||
|
||||
if secretID == nil {
|
||||
return nil, fmt.Errorf("no secret ID provided for login")
|
||||
}
|
||||
|
||||
err := secretID.validate()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid secret ID: %w", err)
|
||||
}
|
||||
|
||||
a := &AppRoleAuth{
|
||||
mountPath: defaultMountPath,
|
||||
roleID: roleID,
|
||||
}
|
||||
|
||||
// secret ID will be read in at login time if it comes from a file or environment variable, in case the underlying value changes
|
||||
if secretID.FromFile != "" {
|
||||
a.secretIDFile = secretID.FromFile
|
||||
}
|
||||
|
||||
if secretID.FromEnv != "" {
|
||||
a.secretIDEnv = secretID.FromEnv
|
||||
}
|
||||
|
||||
if secretID.FromString != "" {
|
||||
a.secretID = secretID.FromString
|
||||
}
|
||||
|
||||
// Loop through each option
|
||||
for _, opt := range opts {
|
||||
// Call the option giving the instantiated
|
||||
// *AppRoleAuth as the argument
|
||||
err := opt(a)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error with login option: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// return the modified auth struct instance
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func (a *AppRoleAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, error) {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
loginData := map[string]interface{}{
|
||||
"role_id": a.roleID,
|
||||
}
|
||||
|
||||
var secretIDValue string
|
||||
|
||||
switch {
|
||||
case a.secretIDFile != "":
|
||||
s, err := a.readSecretIDFromFile()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading secret ID: %w", err)
|
||||
}
|
||||
secretIDValue = s
|
||||
case a.secretIDEnv != "":
|
||||
s := os.Getenv(a.secretIDEnv)
|
||||
if s == "" {
|
||||
return nil, fmt.Errorf("secret ID was specified with an environment variable %q with an empty value", a.secretIDEnv)
|
||||
}
|
||||
secretIDValue = s
|
||||
default:
|
||||
secretIDValue = a.secretID
|
||||
}
|
||||
|
||||
// if the caller indicated that the value was actually a wrapping token, unwrap it first
|
||||
if a.unwrap {
|
||||
unwrappedToken, err := client.Logical().UnwrapWithContext(ctx, secretIDValue)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to unwrap response wrapping token: %w", err)
|
||||
}
|
||||
loginData["secret_id"] = unwrappedToken.Data["secret_id"]
|
||||
} else {
|
||||
loginData["secret_id"] = secretIDValue
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("auth/%s/login", a.mountPath)
|
||||
resp, err := client.Logical().WriteWithContext(ctx, path, loginData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to log in with app role auth: %w", err)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func WithMountPath(mountPath string) LoginOption {
|
||||
return func(a *AppRoleAuth) error {
|
||||
a.mountPath = mountPath
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithWrappingToken() LoginOption {
|
||||
return func(a *AppRoleAuth) error {
|
||||
a.unwrap = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AppRoleAuth) readSecretIDFromFile() (string, error) {
|
||||
secretIDFile, err := os.Open(a.secretIDFile)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to open file containing secret ID: %w", err)
|
||||
}
|
||||
defer secretIDFile.Close()
|
||||
|
||||
limitedReader := io.LimitReader(secretIDFile, 1000)
|
||||
secretIDBytes, err := io.ReadAll(limitedReader)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to read secret ID: %w", err)
|
||||
}
|
||||
|
||||
secretIDValue := strings.TrimSuffix(string(secretIDBytes), "\n")
|
||||
|
||||
return secretIDValue, nil
|
||||
}
|
||||
|
||||
func (secretID *SecretID) validate() error {
|
||||
if secretID.FromFile == "" && secretID.FromEnv == "" && secretID.FromString == "" {
|
||||
return fmt.Errorf("secret ID for AppRole must be provided with a source file, environment variable, or plaintext string")
|
||||
}
|
||||
|
||||
if secretID.FromFile != "" {
|
||||
if secretID.FromEnv != "" || secretID.FromString != "" {
|
||||
return fmt.Errorf("only one source for the secret ID should be specified")
|
||||
}
|
||||
}
|
||||
|
||||
if secretID.FromEnv != "" {
|
||||
if secretID.FromFile != "" || secretID.FromString != "" {
|
||||
return fmt.Errorf("only one source for the secret ID should be specified")
|
||||
}
|
||||
}
|
||||
|
||||
if secretID.FromString != "" {
|
||||
if secretID.FromFile != "" || secretID.FromEnv != "" {
|
||||
return fmt.Errorf("only one source for the secret ID should be specified")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package approle
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/vault/api"
|
||||
)
|
||||
|
||||
// testHTTPServer creates a test HTTP server that handles requests until
|
||||
// the listener returned is closed.
|
||||
func testHTTPServer(
|
||||
t *testing.T, handler http.Handler,
|
||||
) (*api.Config, net.Listener) {
|
||||
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
server := &http.Server{Handler: handler}
|
||||
go server.Serve(ln)
|
||||
|
||||
config := api.DefaultConfig()
|
||||
config.Address = fmt.Sprintf("http://%s", ln.Addr())
|
||||
|
||||
return config, ln
|
||||
}
|
||||
|
||||
func init() {
|
||||
os.Setenv("VAULT_TOKEN", "")
|
||||
}
|
||||
|
||||
func TestLogin(t *testing.T) {
|
||||
secretIDEnvVar := "APPROLE_SECRET_ID"
|
||||
allowedRoleID := "my-role-id"
|
||||
allowedSecretID := "my-secret-id"
|
||||
|
||||
content := []byte(allowedSecretID)
|
||||
tmpfile, err := os.CreateTemp("", "file-containing-secret-id")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating temp file: %v", err)
|
||||
}
|
||||
defer os.Remove(tmpfile.Name()) // clean up
|
||||
err = os.Setenv(secretIDEnvVar, allowedSecretID)
|
||||
if err != nil {
|
||||
t.Fatalf("error writing secret ID to env var: %v", err)
|
||||
}
|
||||
|
||||
if _, err := tmpfile.Write(content); err != nil {
|
||||
t.Fatalf("error writing to temp file: %v", err)
|
||||
}
|
||||
if err := tmpfile.Close(); err != nil {
|
||||
t.Fatalf("error closing temp file: %v", err)
|
||||
}
|
||||
|
||||
// a response to return if the correct values were passed to login
|
||||
authSecret := &api.Secret{
|
||||
Auth: &api.SecretAuth{
|
||||
ClientToken: "a-client-token",
|
||||
},
|
||||
}
|
||||
|
||||
authBytes, err := json.Marshal(authSecret)
|
||||
if err != nil {
|
||||
t.Fatalf("error marshaling json: %v", err)
|
||||
}
|
||||
|
||||
handler := func(w http.ResponseWriter, req *http.Request) {
|
||||
payload := make(map[string]interface{})
|
||||
err := json.NewDecoder(req.Body).Decode(&payload)
|
||||
if err != nil {
|
||||
t.Fatalf("error decoding json: %v", err)
|
||||
}
|
||||
if payload["role_id"] == allowedRoleID && payload["secret_id"] == allowedSecretID {
|
||||
w.Write(authBytes)
|
||||
}
|
||||
}
|
||||
|
||||
config, ln := testHTTPServer(t, http.HandlerFunc(handler))
|
||||
defer ln.Close()
|
||||
|
||||
config.Address = strings.ReplaceAll(config.Address, "127.0.0.1", "localhost")
|
||||
client, err := api.NewClient(config)
|
||||
if err != nil {
|
||||
t.Fatalf("error initializing Vault client: %v", err)
|
||||
}
|
||||
|
||||
authFromFile, err := NewAppRoleAuth(allowedRoleID, &SecretID{FromFile: tmpfile.Name()})
|
||||
if err != nil {
|
||||
t.Fatalf("error initializing AppRoleAuth with secret ID file: %v", err)
|
||||
}
|
||||
|
||||
loginRespFromFile, err := client.Auth().Login(context.TODO(), authFromFile)
|
||||
if err != nil {
|
||||
t.Fatalf("error logging in with secret ID from file: %v", err)
|
||||
}
|
||||
if loginRespFromFile.Auth == nil || loginRespFromFile.Auth.ClientToken == "" {
|
||||
t.Fatalf("no authentication info returned by login")
|
||||
}
|
||||
|
||||
authFromEnv, err := NewAppRoleAuth(allowedRoleID, &SecretID{FromEnv: secretIDEnvVar})
|
||||
if err != nil {
|
||||
t.Fatalf("error initializing AppRoleAuth with secret ID env var: %v", err)
|
||||
}
|
||||
|
||||
loginRespFromEnv, err := client.Auth().Login(context.TODO(), authFromEnv)
|
||||
if err != nil {
|
||||
t.Fatalf("error logging in with secret ID from env var: %v", err)
|
||||
}
|
||||
if loginRespFromEnv.Auth == nil || loginRespFromEnv.Auth.ClientToken == "" {
|
||||
t.Fatalf("no authentication info returned by login with secret ID from env var")
|
||||
}
|
||||
|
||||
authFromStr, err := NewAppRoleAuth(allowedRoleID, &SecretID{FromString: allowedSecretID})
|
||||
if err != nil {
|
||||
t.Fatalf("error initializing AppRoleAuth with secret ID string: %v", err)
|
||||
}
|
||||
|
||||
loginRespFromStr, err := client.Auth().Login(context.TODO(), authFromStr)
|
||||
if err != nil {
|
||||
t.Fatalf("error logging in with string: %v", err)
|
||||
}
|
||||
if loginRespFromStr.Auth == nil || loginRespFromStr.Auth.ClientToken == "" {
|
||||
t.Fatalf("no authentication info returned by login with secret ID from string")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
module github.com/hashicorp/vault/api/auth/approle
|
||||
|
||||
go 1.16
|
||||
|
||||
require github.com/hashicorp/vault/api v1.9.2
|
|
@ -0,0 +1,117 @@
|
|||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c=
|
||||
github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
|
||||
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
|
||||
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v0.16.2 h1:K4ev2ib4LdQETX5cSZBG0DVLk1jwGqSPXBjdah3veNs=
|
||||
github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ=
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/vault/api v1.9.2 h1:YjkZLJ7K3inKgMZ0wzCU9OHqc+UqMQyXsPXnf3Cl2as=
|
||||
github.com/hashicorp/vault/api v1.9.2/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -0,0 +1,287 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package aws
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/ec2metadata"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/go-secure-stdlib/awsutil"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"github.com/hashicorp/vault/api"
|
||||
)
|
||||
|
||||
type AWSAuth struct {
|
||||
// If not provided with the WithRole login option, the Vault server will look for a role
|
||||
// with the friendly name of the IAM principal if using the IAM auth type,
|
||||
// or the name of the EC2 instance's AMI ID if using the EC2 auth type.
|
||||
// If no matching role is found, login will fail.
|
||||
roleName string
|
||||
mountPath string
|
||||
// Can be "iam" or "ec2". Defaults to "iam".
|
||||
authType string
|
||||
// Can be "pkcs7", "identity", or "rsa2048". Defaults to "pkcs7".
|
||||
signatureType string
|
||||
region string
|
||||
iamServerIDHeaderValue string
|
||||
creds *credentials.Credentials
|
||||
nonce string
|
||||
}
|
||||
|
||||
var _ api.AuthMethod = (*AWSAuth)(nil)
|
||||
|
||||
type LoginOption func(a *AWSAuth) error
|
||||
|
||||
const (
|
||||
iamType = "iam"
|
||||
ec2Type = "ec2"
|
||||
pkcs7Type = "pkcs7"
|
||||
identityType = "identity"
|
||||
rsa2048Type = "rsa2048"
|
||||
defaultMountPath = "aws"
|
||||
defaultAuthType = iamType
|
||||
defaultRegion = "us-east-1"
|
||||
defaultSignatureType = pkcs7Type
|
||||
)
|
||||
|
||||
// NewAWSAuth initializes a new AWS auth method interface to be
|
||||
// passed as a parameter to the client.Auth().Login method.
|
||||
//
|
||||
// Supported options: WithRole, WithMountPath, WithIAMAuth, WithEC2Auth,
|
||||
// WithPKCS7Signature, WithIdentitySignature, WithIAMServerIDHeader, WithNonce, WithRegion
|
||||
func NewAWSAuth(opts ...LoginOption) (*AWSAuth, error) {
|
||||
a := &AWSAuth{
|
||||
mountPath: defaultMountPath,
|
||||
authType: defaultAuthType,
|
||||
region: defaultRegion,
|
||||
signatureType: defaultSignatureType,
|
||||
}
|
||||
|
||||
// Loop through each option
|
||||
for _, opt := range opts {
|
||||
// Call the option giving the instantiated
|
||||
// *AWSAuth as the argument
|
||||
err := opt(a)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error with login option: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// return the modified auth struct instance
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// Login sets up the required request body for the AWS auth method's /login
|
||||
// endpoint, and performs a write to it. This method defaults to the "iam"
|
||||
// auth type unless NewAWSAuth is called with WithEC2Auth().
|
||||
//
|
||||
// The Vault client will set its credentials to the values of the
|
||||
// AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION environment
|
||||
// variables. To specify a path to a credentials file on disk instead, set
|
||||
// the environment variable AWS_SHARED_CREDENTIALS_FILE.
|
||||
func (a *AWSAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, error) {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
loginData := make(map[string]interface{})
|
||||
switch a.authType {
|
||||
case ec2Type:
|
||||
sess, err := session.NewSession()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating session to probe EC2 metadata: %w", err)
|
||||
}
|
||||
metadataSvc := ec2metadata.New(sess)
|
||||
if !metadataSvc.Available() {
|
||||
return nil, fmt.Errorf("metadata service not available")
|
||||
}
|
||||
|
||||
if a.signatureType == pkcs7Type {
|
||||
// fetch PKCS #7 signature
|
||||
resp, err := metadataSvc.GetDynamicData("/instance-identity/pkcs7")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get PKCS 7 data from metadata service: %w", err)
|
||||
}
|
||||
pkcs7 := strings.TrimSpace(resp)
|
||||
loginData["pkcs7"] = pkcs7
|
||||
} else if a.signatureType == identityType {
|
||||
// fetch signature from identity document
|
||||
doc, err := metadataSvc.GetDynamicData("/instance-identity/document")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error requesting instance identity doc: %w", err)
|
||||
}
|
||||
loginData["identity"] = base64.StdEncoding.EncodeToString([]byte(doc))
|
||||
|
||||
signature, err := metadataSvc.GetDynamicData("/instance-identity/signature")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error requesting signature: %w", err)
|
||||
}
|
||||
loginData["signature"] = signature
|
||||
} else if a.signatureType == rsa2048Type {
|
||||
// fetch RSA 2048 signature, which is also a PKCS#7 signature
|
||||
resp, err := metadataSvc.GetDynamicData("/instance-identity/rsa2048")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get PKCS 7 data from metadata service: %w", err)
|
||||
}
|
||||
pkcs7 := strings.TrimSpace(resp)
|
||||
loginData["pkcs7"] = pkcs7
|
||||
} else {
|
||||
return nil, fmt.Errorf("unknown signature type: %s", a.signatureType)
|
||||
}
|
||||
|
||||
// Add the reauthentication value, if we have one
|
||||
if a.nonce == "" {
|
||||
uid, err := uuid.GenerateUUID()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating uuid for reauthentication value: %w", err)
|
||||
}
|
||||
a.nonce = uid
|
||||
}
|
||||
loginData["nonce"] = a.nonce
|
||||
case iamType:
|
||||
logger := hclog.Default()
|
||||
if a.creds == nil {
|
||||
credsConfig := awsutil.CredentialsConfig{
|
||||
AccessKey: os.Getenv("AWS_ACCESS_KEY_ID"),
|
||||
SecretKey: os.Getenv("AWS_SECRET_ACCESS_KEY"),
|
||||
SessionToken: os.Getenv("AWS_SESSION_TOKEN"),
|
||||
Logger: logger,
|
||||
}
|
||||
|
||||
// the env vars above will take precedence if they are set, as
|
||||
// they will be added to the ChainProvider stack first
|
||||
var hasCredsFile bool
|
||||
credsFilePath := os.Getenv("AWS_SHARED_CREDENTIALS_FILE")
|
||||
if credsFilePath != "" {
|
||||
hasCredsFile = true
|
||||
credsConfig.Filename = credsFilePath
|
||||
}
|
||||
|
||||
creds, err := credsConfig.GenerateCredentialChain(awsutil.WithSharedCredentials(hasCredsFile))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if creds == nil {
|
||||
return nil, fmt.Errorf("could not compile valid credential providers from static config, environment, shared, or instance metadata")
|
||||
}
|
||||
|
||||
_, err = creds.Get()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to retrieve credentials from credential chain: %w", err)
|
||||
}
|
||||
|
||||
a.creds = creds
|
||||
}
|
||||
|
||||
data, err := awsutil.GenerateLoginData(a.creds, a.iamServerIDHeaderValue, a.region, logger)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to generate login data for AWS auth endpoint: %w", err)
|
||||
}
|
||||
loginData = data
|
||||
}
|
||||
|
||||
// Add role if we have one. If not, Vault will infer the role name based
|
||||
// on the IAM friendly name (iam auth type) or EC2 instance's
|
||||
// AMI ID (ec2 auth type).
|
||||
if a.roleName != "" {
|
||||
loginData["role"] = a.roleName
|
||||
}
|
||||
|
||||
if a.iamServerIDHeaderValue != "" {
|
||||
client.AddHeader("iam_server_id_header_value", a.iamServerIDHeaderValue)
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("auth/%s/login", a.mountPath)
|
||||
resp, err := client.Logical().WriteWithContext(ctx, path, loginData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to log in with AWS auth: %w", err)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func WithRole(roleName string) LoginOption {
|
||||
return func(a *AWSAuth) error {
|
||||
a.roleName = roleName
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithMountPath(mountPath string) LoginOption {
|
||||
return func(a *AWSAuth) error {
|
||||
a.mountPath = mountPath
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithEC2Auth() LoginOption {
|
||||
return func(a *AWSAuth) error {
|
||||
a.authType = ec2Type
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithIAMAuth() LoginOption {
|
||||
return func(a *AWSAuth) error {
|
||||
a.authType = iamType
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithIdentitySignature will have the client send the cryptographic identity
|
||||
// document signature to verify EC2 auth logins. Only used by EC2 auth type.
|
||||
// If this option is not provided, will default to using the PKCS #7 signature.
|
||||
// The signature type used should match the type of the public AWS cert Vault
|
||||
// has been configured with to verify EC2 instance identity.
|
||||
// https://www.vaultproject.io/api/auth/aws#create-certificate-configuration
|
||||
func WithIdentitySignature() LoginOption {
|
||||
return func(a *AWSAuth) error {
|
||||
a.signatureType = identityType
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithPKCS7Signature will explicitly tell the client to send the PKCS #7
|
||||
// signature to verify EC2 auth logins. Only used by EC2 auth type.
|
||||
// PKCS #7 is the default, but this method is provided for additional clarity.
|
||||
// The signature type used should match the type of the public AWS cert Vault
|
||||
// has been configured with to verify EC2 instance identity.
|
||||
// https://www.vaultproject.io/api/auth/aws#create-certificate-configuration
|
||||
func WithPKCS7Signature() LoginOption {
|
||||
return func(a *AWSAuth) error {
|
||||
a.signatureType = pkcs7Type
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithIAMServerIDHeader(headerValue string) LoginOption {
|
||||
return func(a *AWSAuth) error {
|
||||
a.iamServerIDHeaderValue = headerValue
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithNonce can be used to specify a named nonce for the ec2 auth login
|
||||
// method. If not provided, an automatically-generated uuid will be used
|
||||
// instead.
|
||||
func WithNonce(nonce string) LoginOption {
|
||||
return func(a *AWSAuth) error {
|
||||
a.nonce = nonce
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithRegion(region string) LoginOption {
|
||||
return func(a *AWSAuth) error {
|
||||
a.region = region
|
||||
return nil
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
module github.com/hashicorp/vault/api/auth/aws
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/aws/aws-sdk-go v1.30.27
|
||||
github.com/hashicorp/go-hclog v0.16.2
|
||||
github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6
|
||||
github.com/hashicorp/go-uuid v1.0.2
|
||||
github.com/hashicorp/vault/api v1.9.2
|
||||
)
|
|
@ -0,0 +1,142 @@
|
|||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/aws/aws-sdk-go v1.30.27 h1:9gPjZWVDSoQrBO2AvqrWObS6KAZByfEJxQoCYo4ZfK0=
|
||||
github.com/aws/aws-sdk-go v1.30.27/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c=
|
||||
github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
|
||||
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
|
||||
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v0.16.2 h1:K4ev2ib4LdQETX5cSZBG0DVLk1jwGqSPXBjdah3veNs=
|
||||
github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6 h1:W9WN8p6moV1fjKLkeqEgkAMu5rauy9QeYDAmIaPuuiA=
|
||||
github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6/go.mod h1:MpCPSPGLDILGb4JMm94/mMi3YysIqsXzGCzkEZjcjXg=
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ=
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
||||
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
|
||||
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/vault/api v1.9.2 h1:YjkZLJ7K3inKgMZ0wzCU9OHqc+UqMQyXsPXnf3Cl2as=
|
||||
github.com/hashicorp/vault/api v1.9.2/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8=
|
||||
github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=
|
||||
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -0,0 +1,246 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package azure
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/vault/api"
|
||||
)
|
||||
|
||||
type AzureAuth struct {
|
||||
roleName string
|
||||
mountPath string
|
||||
resource string
|
||||
}
|
||||
|
||||
var _ api.AuthMethod = (*AzureAuth)(nil)
|
||||
|
||||
type LoginOption func(a *AzureAuth) error
|
||||
|
||||
type responseJSON struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
ExpiresIn string `json:"expires_in"`
|
||||
ExpiresOn string `json:"expires_on"`
|
||||
NotBefore string `json:"not_before"`
|
||||
Resource string `json:"resource"`
|
||||
TokenType string `json:"token_type"`
|
||||
}
|
||||
|
||||
type errorJSON struct {
|
||||
Error string `json:"error"`
|
||||
ErrorDescription string `json:"error_description"`
|
||||
}
|
||||
|
||||
type metadataJSON struct {
|
||||
Compute computeJSON `json:"compute"`
|
||||
}
|
||||
|
||||
type computeJSON struct {
|
||||
VMName string `json:"name"`
|
||||
VMScaleSetName string `json:"vmScaleSetName"`
|
||||
SubscriptionID string `json:"subscriptionId"`
|
||||
ResourceGroupName string `json:"resourceGroupName"`
|
||||
}
|
||||
|
||||
const (
|
||||
defaultMountPath = "azure"
|
||||
defaultResourceURL = "https://management.azure.com/"
|
||||
metadataEndpoint = "http://169.254.169.254"
|
||||
metadataAPIVersion = "2021-05-01"
|
||||
apiVersionQueryParam = "api-version"
|
||||
resourceQueryParam = "resource"
|
||||
clientTimeout = 10 * time.Second
|
||||
)
|
||||
|
||||
// NewAzureAuth initializes a new Azure auth method interface to be
|
||||
// passed as a parameter to the client.Auth().Login method.
|
||||
//
|
||||
// Supported options: WithMountPath, WithResource
|
||||
func NewAzureAuth(roleName string, opts ...LoginOption) (*AzureAuth, error) {
|
||||
if roleName == "" {
|
||||
return nil, fmt.Errorf("no role name provided for login")
|
||||
}
|
||||
|
||||
a := &AzureAuth{
|
||||
roleName: roleName,
|
||||
mountPath: defaultMountPath,
|
||||
resource: defaultResourceURL,
|
||||
}
|
||||
|
||||
// Loop through each option
|
||||
for _, opt := range opts {
|
||||
// Call the option giving the instantiated
|
||||
// *AzureAuth as the argument
|
||||
err := opt(a)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error with login option: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// return the modified auth struct instance
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// Login sets up the required request body for the Azure auth method's /login
|
||||
// endpoint, and performs a write to it.
|
||||
func (a *AzureAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, error) {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
jwtResp, err := a.getJWT()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get access token: %w", err)
|
||||
}
|
||||
|
||||
metadataRespJSON, err := getMetadata()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get instance metadata: %w", err)
|
||||
}
|
||||
|
||||
loginData := map[string]interface{}{
|
||||
"role": a.roleName,
|
||||
"jwt": jwtResp,
|
||||
"vm_name": metadataRespJSON.Compute.VMName,
|
||||
"vmss_name": metadataRespJSON.Compute.VMScaleSetName,
|
||||
"subscription_id": metadataRespJSON.Compute.SubscriptionID,
|
||||
"resource_group_name": metadataRespJSON.Compute.ResourceGroupName,
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("auth/%s/login", a.mountPath)
|
||||
resp, err := client.Logical().WriteWithContext(ctx, path, loginData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to log in with Azure auth: %w", err)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func WithMountPath(mountPath string) LoginOption {
|
||||
return func(a *AzureAuth) error {
|
||||
a.mountPath = mountPath
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithResource allows you to specify a different resource URL to use as the aud value
|
||||
// on the JWT token than the default of Azure Public Cloud's ARM URL.
|
||||
// This should match the resource URI that an administrator configured your
|
||||
// Vault server to use.
|
||||
//
|
||||
// See https://github.com/Azure/go-autorest/blob/master/autorest/azure/environments.go
|
||||
// for a list of valid environments.
|
||||
func WithResource(url string) LoginOption {
|
||||
return func(a *AzureAuth) error {
|
||||
a.resource = url
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieves an access token from Managed Identities for Azure Resources
|
||||
//
|
||||
// Learn more here: https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/how-to-use-vm-token
|
||||
func (a *AzureAuth) getJWT() (string, error) {
|
||||
identityEndpoint, err := url.Parse(fmt.Sprintf("%s/metadata/identity/oauth2/token", metadataEndpoint))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error creating metadata URL: %w", err)
|
||||
}
|
||||
|
||||
identityParameters := identityEndpoint.Query()
|
||||
identityParameters.Add(apiVersionQueryParam, metadataAPIVersion)
|
||||
identityParameters.Add(resourceQueryParam, a.resource)
|
||||
identityEndpoint.RawQuery = identityParameters.Encode()
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, identityEndpoint.String(), nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error creating HTTP request: %w", err)
|
||||
}
|
||||
req.Header.Add("Metadata", "true")
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: clientTimeout,
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error calling Azure token endpoint: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
responseBytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error reading response body from Azure token endpoint: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
var errResp errorJSON
|
||||
err = json.Unmarshal(responseBytes, &errResp)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("received error message but was unable to unmarshal its contents")
|
||||
}
|
||||
return "", fmt.Errorf("%s error from Azure token endpoint: %s", errResp.Error, errResp.ErrorDescription)
|
||||
}
|
||||
|
||||
var r responseJSON
|
||||
err = json.Unmarshal(responseBytes, &r)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error unmarshaling response from Azure token endpoint: %w", err)
|
||||
}
|
||||
|
||||
return r.AccessToken, nil
|
||||
}
|
||||
|
||||
func getMetadata() (metadataJSON, error) {
|
||||
metadataEndpoint, err := url.Parse(fmt.Sprintf("%s/metadata/instance", metadataEndpoint))
|
||||
if err != nil {
|
||||
return metadataJSON{}, err
|
||||
}
|
||||
|
||||
metadataParameters := metadataEndpoint.Query()
|
||||
metadataParameters.Add(apiVersionQueryParam, metadataAPIVersion)
|
||||
metadataEndpoint.RawQuery = metadataParameters.Encode()
|
||||
req, err := http.NewRequest(http.MethodGet, metadataEndpoint.String(), nil)
|
||||
if err != nil {
|
||||
return metadataJSON{}, fmt.Errorf("error creating HTTP Request for metadata endpoint: %w", err)
|
||||
}
|
||||
req.Header.Add("Metadata", "true")
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: clientTimeout,
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return metadataJSON{}, fmt.Errorf("error calling metadata endpoint: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
responseBytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return metadataJSON{}, fmt.Errorf("error reading response body from metadata endpoint: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
var errResp errorJSON
|
||||
_ = json.Unmarshal(responseBytes, &errResp)
|
||||
if err != nil {
|
||||
return metadataJSON{}, fmt.Errorf("received error message but was unable to unmarshal its contents")
|
||||
}
|
||||
return metadataJSON{}, fmt.Errorf("%s error from metadata endpoint: %s", errResp.Error, errResp.ErrorDescription)
|
||||
}
|
||||
|
||||
var r metadataJSON
|
||||
err = json.Unmarshal(responseBytes, &r)
|
||||
if err != nil {
|
||||
return metadataJSON{}, fmt.Errorf("error unmarshaling the response from metadata endpoint: %w", err)
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
module github.com/hashicorp/vault/api/auth/azure
|
||||
|
||||
go 1.16
|
||||
|
||||
require github.com/hashicorp/vault/api v1.9.2
|
|
@ -0,0 +1,117 @@
|
|||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c=
|
||||
github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
|
||||
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
|
||||
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v0.16.2 h1:K4ev2ib4LdQETX5cSZBG0DVLk1jwGqSPXBjdah3veNs=
|
||||
github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ=
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/vault/api v1.9.2 h1:YjkZLJ7K3inKgMZ0wzCU9OHqc+UqMQyXsPXnf3Cl2as=
|
||||
github.com/hashicorp/vault/api v1.9.2/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -0,0 +1,191 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package gcp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"cloud.google.com/go/compute/metadata"
|
||||
credentials "cloud.google.com/go/iam/credentials/apiv1"
|
||||
"github.com/hashicorp/vault/api"
|
||||
credentialspb "google.golang.org/genproto/googleapis/iam/credentials/v1"
|
||||
)
|
||||
|
||||
type GCPAuth struct {
|
||||
roleName string
|
||||
mountPath string
|
||||
authType string
|
||||
serviceAccountEmail string
|
||||
}
|
||||
|
||||
var _ api.AuthMethod = (*GCPAuth)(nil)
|
||||
|
||||
type LoginOption func(a *GCPAuth) error
|
||||
|
||||
const (
|
||||
iamType = "iam"
|
||||
gceType = "gce"
|
||||
defaultMountPath = "gcp"
|
||||
defaultAuthType = gceType
|
||||
identityMetadataURL = "http://metadata/computeMetadata/v1/instance/service-accounts/default/identity"
|
||||
)
|
||||
|
||||
// NewGCPAuth initializes a new GCP auth method interface to be
|
||||
// passed as a parameter to the client.Auth().Login method.
|
||||
//
|
||||
// Supported options: WithMountPath, WithIAMAuth, WithGCEAuth
|
||||
func NewGCPAuth(roleName string, opts ...LoginOption) (*GCPAuth, error) {
|
||||
if roleName == "" {
|
||||
return nil, fmt.Errorf("no role name provided for login")
|
||||
}
|
||||
|
||||
a := &GCPAuth{
|
||||
mountPath: defaultMountPath,
|
||||
authType: defaultAuthType,
|
||||
roleName: roleName,
|
||||
}
|
||||
|
||||
// Loop through each option
|
||||
for _, opt := range opts {
|
||||
// Call the option giving the instantiated
|
||||
// *GCPAuth as the argument
|
||||
err := opt(a)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error with login option: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// return the modified auth struct instance
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// Login sets up the required request body for the GCP auth method's /login
|
||||
// endpoint, and performs a write to it. This method defaults to the "gce"
|
||||
// auth type unless NewGCPAuth is called with WithIAMAuth().
|
||||
func (a *GCPAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, error) {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
loginData := map[string]interface{}{
|
||||
"role": a.roleName,
|
||||
}
|
||||
switch a.authType {
|
||||
case gceType:
|
||||
jwt, err := a.getJWTFromMetadataService(client.Address())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to retrieve JWT from GCE metadata service: %w", err)
|
||||
}
|
||||
loginData["jwt"] = jwt
|
||||
case iamType:
|
||||
jwtResp, err := a.signJWT()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to sign JWT for authenticating to GCP: %w", err)
|
||||
}
|
||||
loginData["jwt"] = jwtResp.SignedJwt
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("auth/%s/login", a.mountPath)
|
||||
resp, err := client.Logical().WriteWithContext(ctx, path, loginData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to log in with GCP auth: %w", err)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func WithMountPath(mountPath string) LoginOption {
|
||||
return func(a *GCPAuth) error {
|
||||
a.mountPath = mountPath
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithIAMAuth(serviceAccountEmail string) LoginOption {
|
||||
return func(a *GCPAuth) error {
|
||||
a.serviceAccountEmail = serviceAccountEmail
|
||||
a.authType = iamType
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithGCEAuth() LoginOption {
|
||||
return func(a *GCPAuth) error {
|
||||
a.authType = gceType
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// generate signed JWT token from GCP IAM.
|
||||
func (a *GCPAuth) signJWT() (*credentialspb.SignJwtResponse, error) {
|
||||
ctx := context.Background()
|
||||
iamClient, err := credentials.NewIamCredentialsClient(ctx) // can pass option.WithCredentialsFile("path/to/creds.json") as second param if GOOGLE_APPLICATION_CREDENTIALS env var not set
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to initialize IAM credentials client: %w", err)
|
||||
}
|
||||
defer iamClient.Close()
|
||||
|
||||
resourceName := fmt.Sprintf("projects/-/serviceAccounts/%s", a.serviceAccountEmail)
|
||||
jwtPayload := map[string]interface{}{
|
||||
"aud": fmt.Sprintf("vault/%s", a.roleName),
|
||||
"sub": a.serviceAccountEmail,
|
||||
"exp": time.Now().Add(time.Minute * 10).Unix(),
|
||||
}
|
||||
|
||||
payloadBytes, err := json.Marshal(jwtPayload)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to marshal jwt payload to json: %w", err)
|
||||
}
|
||||
|
||||
signJWTReq := &credentialspb.SignJwtRequest{
|
||||
Name: resourceName,
|
||||
Payload: string(payloadBytes),
|
||||
}
|
||||
|
||||
jwtResp, err := iamClient.SignJwt(ctx, signJWTReq)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to sign JWT: %w", err)
|
||||
}
|
||||
|
||||
return jwtResp, nil
|
||||
}
|
||||
|
||||
func (a *GCPAuth) getJWTFromMetadataService(vaultAddress string) (string, error) {
|
||||
if !metadata.OnGCE() {
|
||||
return "", fmt.Errorf("GCE metadata service not available")
|
||||
}
|
||||
|
||||
// build request to metadata server
|
||||
c := &http.Client{}
|
||||
req, err := http.NewRequest(http.MethodGet, identityMetadataURL, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error creating http request: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Add("Metadata-Flavor", "Google")
|
||||
q := url.Values{}
|
||||
q.Add("audience", fmt.Sprintf("%s/vault/%s", vaultAddress, a.roleName))
|
||||
q.Add("format", "full")
|
||||
req.URL.RawQuery = q.Encode()
|
||||
resp, err := c.Do(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error making request to metadata service: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// get jwt from response
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
jwt := string(body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error reading response from metadata service: %w", err)
|
||||
}
|
||||
|
||||
return jwt, nil
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
module github.com/hashicorp/vault/api/auth/gcp
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.97.0
|
||||
github.com/hashicorp/vault/api v1.9.2
|
||||
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0
|
||||
google.golang.org/grpc v1.41.0 // indirect
|
||||
)
|
|
@ -0,0 +1,641 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
|
||||
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
|
||||
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
|
||||
cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
|
||||
cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
|
||||
cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
|
||||
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
|
||||
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
|
||||
cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
|
||||
cloud.google.com/go v0.97.0 h1:3DXvAyifywvq64LfkKaMOmkWPS1CikIQdMe2lY9vxU8=
|
||||
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c=
|
||||
github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
|
||||
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
|
||||
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gax-go/v2 v2.1.0 h1:6DWmvNpomjL1+3liNSZbVns3zsYzzCjm6pRBO1tLeso=
|
||||
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v0.16.2 h1:K4ev2ib4LdQETX5cSZBG0DVLk1jwGqSPXBjdah3veNs=
|
||||
github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ=
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/vault/api v1.9.2 h1:YjkZLJ7K3inKgMZ0wzCU9OHqc+UqMQyXsPXnf3Cl2as=
|
||||
github.com/hashicorp/vault/api v1.9.2/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f h1:Qmd2pbz05z7z6lm0DrgQVVPuBm92jqujBKMHMOlOQEw=
|
||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
|
||||
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
|
||||
google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
|
||||
google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
|
||||
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
|
||||
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
|
||||
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
|
||||
google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
|
||||
google.golang.org/api v0.57.0 h1:4t9zuDlHLcIx0ZEhmXEeFVCRsiOgpgn2QOH9N0MNjPI=
|
||||
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
||||
google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
|
||||
google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
|
||||
google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
|
||||
google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
|
||||
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0 h1:5Tbluzus3QxoAJx4IefGt1W0HQZW4nuMrVk684jI74Q=
|
||||
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.41.0 h1:f+PlOh7QV4iIJkPrx5NQ7qaNGFQ3OTse67yaDHfju4E=
|
||||
google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
|
@ -0,0 +1,5 @@
|
|||
module github.com/hashicorp/vault/api/auth/kubernetes
|
||||
|
||||
go 1.16
|
||||
|
||||
require github.com/hashicorp/vault/api v1.9.2
|
|
@ -0,0 +1,117 @@
|
|||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c=
|
||||
github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
|
||||
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
|
||||
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v0.16.2 h1:K4ev2ib4LdQETX5cSZBG0DVLk1jwGqSPXBjdah3veNs=
|
||||
github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ=
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/vault/api v1.9.2 h1:YjkZLJ7K3inKgMZ0wzCU9OHqc+UqMQyXsPXnf3Cl2as=
|
||||
github.com/hashicorp/vault/api v1.9.2/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -0,0 +1,136 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package kubernetes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/vault/api"
|
||||
)
|
||||
|
||||
type KubernetesAuth struct {
|
||||
roleName string
|
||||
mountPath string
|
||||
serviceAccountToken string
|
||||
}
|
||||
|
||||
var _ api.AuthMethod = (*KubernetesAuth)(nil)
|
||||
|
||||
type LoginOption func(a *KubernetesAuth) error
|
||||
|
||||
const (
|
||||
defaultMountPath = "kubernetes"
|
||||
defaultServiceAccountTokenPath = "/var/run/secrets/kubernetes.io/serviceaccount/token"
|
||||
)
|
||||
|
||||
// NewKubernetesAuth creates a KubernetesAuth struct which can be passed to
|
||||
// the client.Auth().Login method to authenticate to Vault. The roleName
|
||||
// parameter should be the name of the role in Vault that was created with
|
||||
// this app's Kubernetes service account bound to it.
|
||||
//
|
||||
// The Kubernetes service account token JWT is retrieved from
|
||||
// /var/run/secrets/kubernetes.io/serviceaccount/token by default. To change this
|
||||
// path, pass the WithServiceAccountTokenPath option. To instead pass the
|
||||
// JWT directly as a string, or to read the value from an environment
|
||||
// variable, use WithServiceAccountToken and WithServiceAccountTokenEnv respectively.
|
||||
//
|
||||
// Supported options: WithMountPath, WithServiceAccountTokenPath, WithServiceAccountTokenEnv, WithServiceAccountToken
|
||||
func NewKubernetesAuth(roleName string, opts ...LoginOption) (*KubernetesAuth, error) {
|
||||
if roleName == "" {
|
||||
return nil, fmt.Errorf("no role name was provided")
|
||||
}
|
||||
|
||||
a := &KubernetesAuth{
|
||||
roleName: roleName,
|
||||
mountPath: defaultMountPath,
|
||||
}
|
||||
|
||||
// Loop through each option
|
||||
for _, opt := range opts {
|
||||
// Call the option giving the instantiated
|
||||
// *KubernetesAuth as the argument
|
||||
err := opt(a)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error with login option: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if a.serviceAccountToken == "" {
|
||||
token, err := readTokenFromFile(defaultServiceAccountTokenPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading service account token from default location: %w", err)
|
||||
}
|
||||
a.serviceAccountToken = token
|
||||
}
|
||||
|
||||
// return the modified auth struct instance
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func (a *KubernetesAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, error) {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
loginData := map[string]interface{}{
|
||||
"jwt": a.serviceAccountToken,
|
||||
"role": a.roleName,
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("auth/%s/login", a.mountPath)
|
||||
resp, err := client.Logical().WriteWithContext(ctx, path, loginData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to log in with Kubernetes auth: %w", err)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func WithMountPath(mountPath string) LoginOption {
|
||||
return func(a *KubernetesAuth) error {
|
||||
a.mountPath = mountPath
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithServiceAccountTokenPath allows you to specify a different path to
|
||||
// where your application's Kubernetes service account token is mounted,
|
||||
// instead of the default of /var/run/secrets/kubernetes.io/serviceaccount/token
|
||||
func WithServiceAccountTokenPath(pathToToken string) LoginOption {
|
||||
return func(a *KubernetesAuth) error {
|
||||
token, err := readTokenFromFile(pathToToken)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to read service account token from file: %w", err)
|
||||
}
|
||||
a.serviceAccountToken = token
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithServiceAccountToken(jwt string) LoginOption {
|
||||
return func(a *KubernetesAuth) error {
|
||||
a.serviceAccountToken = jwt
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithServiceAccountTokenEnv(envVar string) LoginOption {
|
||||
return func(a *KubernetesAuth) error {
|
||||
token := os.Getenv(envVar)
|
||||
if token == "" {
|
||||
return fmt.Errorf("service account token was specified with an environment variable with an empty value")
|
||||
}
|
||||
a.serviceAccountToken = token
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func readTokenFromFile(filepath string) (string, error) {
|
||||
jwt, err := os.ReadFile(filepath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to read file containing service account token: %w", err)
|
||||
}
|
||||
return string(jwt), nil
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
module github.com/hashicorp/vault/api/auth/ldap
|
||||
|
||||
go 1.16
|
||||
|
||||
require github.com/hashicorp/vault/api v1.9.2
|
|
@ -0,0 +1,117 @@
|
|||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c=
|
||||
github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
|
||||
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
|
||||
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v0.16.2 h1:K4ev2ib4LdQETX5cSZBG0DVLk1jwGqSPXBjdah3veNs=
|
||||
github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ=
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/vault/api v1.9.2 h1:YjkZLJ7K3inKgMZ0wzCU9OHqc+UqMQyXsPXnf3Cl2as=
|
||||
github.com/hashicorp/vault/api v1.9.2/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -0,0 +1,169 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package ldap
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/vault/api"
|
||||
)
|
||||
|
||||
type LDAPAuth struct {
|
||||
mountPath string
|
||||
username string
|
||||
password string
|
||||
passwordFile string
|
||||
passwordEnv string
|
||||
}
|
||||
|
||||
type Password struct {
|
||||
// Path on the file system where the LDAP password can be found.
|
||||
FromFile string
|
||||
// The name of the environment variable containing the LDAP password
|
||||
FromEnv string
|
||||
// The password as a plaintext string value.
|
||||
FromString string
|
||||
}
|
||||
|
||||
var _ api.AuthMethod = (*LDAPAuth)(nil)
|
||||
|
||||
type LoginOption func(a *LDAPAuth) error
|
||||
|
||||
const (
|
||||
defaultMountPath = "ldap"
|
||||
)
|
||||
|
||||
// NewLDAPAuth initializes a new LDAP auth method interface to be
|
||||
// passed as a parameter to the client.Auth().Login method.
|
||||
//
|
||||
// Supported options: WithMountPath
|
||||
func NewLDAPAuth(username string, password *Password, opts ...LoginOption) (*LDAPAuth, error) {
|
||||
if username == "" {
|
||||
return nil, fmt.Errorf("no user name provided for login")
|
||||
}
|
||||
|
||||
if password == nil {
|
||||
return nil, fmt.Errorf("no password provided for login")
|
||||
}
|
||||
|
||||
err := password.validate()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid password: %w", err)
|
||||
}
|
||||
|
||||
a := &LDAPAuth{
|
||||
mountPath: defaultMountPath,
|
||||
username: username,
|
||||
}
|
||||
|
||||
if password.FromFile != "" {
|
||||
a.passwordFile = password.FromFile
|
||||
}
|
||||
|
||||
if password.FromEnv != "" {
|
||||
a.passwordEnv = password.FromEnv
|
||||
}
|
||||
|
||||
if password.FromString != "" {
|
||||
a.password = password.FromString
|
||||
}
|
||||
// Loop through each option
|
||||
for _, opt := range opts {
|
||||
// Call the option giving the instantiated
|
||||
// *LDAPAuth as the argument
|
||||
err := opt(a)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error with login option: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// return the modified auth struct instance
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func (a *LDAPAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, error) {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
loginData := make(map[string]interface{})
|
||||
|
||||
if a.passwordFile != "" {
|
||||
passwordValue, err := a.readPasswordFromFile()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading password: %w", err)
|
||||
}
|
||||
loginData["password"] = passwordValue
|
||||
} else if a.passwordEnv != "" {
|
||||
passwordValue := os.Getenv(a.passwordEnv)
|
||||
if passwordValue == "" {
|
||||
return nil, fmt.Errorf("password was specified with an environment variable with an empty value")
|
||||
}
|
||||
loginData["password"] = passwordValue
|
||||
} else {
|
||||
loginData["password"] = a.password
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("auth/%s/login/%s", a.mountPath, a.username)
|
||||
resp, err := client.Logical().WriteWithContext(ctx, path, loginData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to log in with LDAP auth: %w", err)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func WithMountPath(mountPath string) LoginOption {
|
||||
return func(a *LDAPAuth) error {
|
||||
a.mountPath = mountPath
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a *LDAPAuth) readPasswordFromFile() (string, error) {
|
||||
passwordFile, err := os.Open(a.passwordFile)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to open file containing password: %w", err)
|
||||
}
|
||||
defer passwordFile.Close()
|
||||
|
||||
limitedReader := io.LimitReader(passwordFile, 1000)
|
||||
passwordBytes, err := io.ReadAll(limitedReader)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to read password: %w", err)
|
||||
}
|
||||
|
||||
passwordValue := strings.TrimSuffix(string(passwordBytes), "\n")
|
||||
|
||||
return passwordValue, nil
|
||||
}
|
||||
|
||||
func (password *Password) validate() error {
|
||||
if password.FromFile == "" && password.FromEnv == "" && password.FromString == "" {
|
||||
return fmt.Errorf("password for LDAP auth must be provided with a source file, environment variable, or plaintext string")
|
||||
}
|
||||
|
||||
if password.FromFile != "" {
|
||||
if password.FromEnv != "" || password.FromString != "" {
|
||||
return fmt.Errorf("only one source for the password should be specified")
|
||||
}
|
||||
}
|
||||
|
||||
if password.FromEnv != "" {
|
||||
if password.FromFile != "" || password.FromString != "" {
|
||||
return fmt.Errorf("only one source for the password should be specified")
|
||||
}
|
||||
}
|
||||
|
||||
if password.FromString != "" {
|
||||
if password.FromFile != "" || password.FromEnv != "" {
|
||||
return fmt.Errorf("only one source for the password should be specified")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package ldap
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/vault/api"
|
||||
)
|
||||
|
||||
// testHTTPServer creates a test HTTP server that handles requests until
|
||||
// the listener returned is closed.
|
||||
func testHTTPServer(
|
||||
t *testing.T, handler http.Handler,
|
||||
) (*api.Config, net.Listener) {
|
||||
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
server := &http.Server{Handler: handler}
|
||||
go server.Serve(ln)
|
||||
|
||||
config := api.DefaultConfig()
|
||||
config.Address = fmt.Sprintf("http://%s", ln.Addr())
|
||||
|
||||
return config, ln
|
||||
}
|
||||
|
||||
func init() {
|
||||
os.Setenv("VAULT_TOKEN", "")
|
||||
}
|
||||
|
||||
func TestLogin(t *testing.T) {
|
||||
passwordEnvVar := "LDAP_PASSWORD"
|
||||
allowedPassword := "6hrtL!*bro!ywbQbvDwW"
|
||||
|
||||
content := []byte(allowedPassword)
|
||||
tmpfile, err := os.CreateTemp("./", "file-containing-password")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating temp file: %v", err)
|
||||
}
|
||||
defer os.Remove(tmpfile.Name()) // clean up
|
||||
err = os.Setenv(passwordEnvVar, allowedPassword)
|
||||
if err != nil {
|
||||
t.Fatalf("error writing password to env var: %v", err)
|
||||
}
|
||||
|
||||
if _, err := tmpfile.Write(content); err != nil {
|
||||
t.Fatalf("error writing to temp file: %v", err)
|
||||
}
|
||||
if err := tmpfile.Close(); err != nil {
|
||||
t.Fatalf("error closing temp file: %v", err)
|
||||
}
|
||||
|
||||
// a response to return if the correct values were passed to login
|
||||
authSecret := &api.Secret{
|
||||
Auth: &api.SecretAuth{
|
||||
ClientToken: "a-client-token",
|
||||
},
|
||||
}
|
||||
|
||||
authBytes, err := json.Marshal(authSecret)
|
||||
if err != nil {
|
||||
t.Fatalf("error marshaling json: %v", err)
|
||||
}
|
||||
|
||||
handler := func(w http.ResponseWriter, req *http.Request) {
|
||||
payload := make(map[string]interface{})
|
||||
err := json.NewDecoder(req.Body).Decode(&payload)
|
||||
if err != nil {
|
||||
t.Fatalf("error decoding json: %v", err)
|
||||
}
|
||||
if payload["password"] == allowedPassword {
|
||||
w.Write(authBytes)
|
||||
}
|
||||
}
|
||||
|
||||
config, ln := testHTTPServer(t, http.HandlerFunc(handler))
|
||||
defer ln.Close()
|
||||
|
||||
config.Address = strings.ReplaceAll(config.Address, "127.0.0.1", "localhost")
|
||||
client, err := api.NewClient(config)
|
||||
if err != nil {
|
||||
t.Fatalf("error initializing Vault client: %v", err)
|
||||
}
|
||||
|
||||
// Password fromFile test
|
||||
authFromFile, err := NewLDAPAuth("my-ldap-username", &Password{FromFile: tmpfile.Name()})
|
||||
if err != nil {
|
||||
t.Fatalf("error initializing LDAPAuth with password file: %v", err)
|
||||
}
|
||||
|
||||
loginRespFromFile, err := client.Auth().Login(context.TODO(), authFromFile)
|
||||
if err != nil {
|
||||
t.Fatalf("error logging in with password from file: %v", err)
|
||||
}
|
||||
|
||||
if loginRespFromFile.Auth == nil || loginRespFromFile.Auth.ClientToken == "" {
|
||||
t.Fatalf("no authentication info returned by login")
|
||||
}
|
||||
|
||||
// Password fromEnv Test
|
||||
authFromEnv, err := NewLDAPAuth("my-ldap-username", &Password{FromEnv: passwordEnvVar})
|
||||
if err != nil {
|
||||
t.Fatalf("error initializing LDAPAuth with password env var: %v", err)
|
||||
}
|
||||
|
||||
loginRespFromEnv, err := client.Auth().Login(context.TODO(), authFromEnv)
|
||||
if err != nil {
|
||||
t.Fatalf("error logging in with password from env var: %v", err)
|
||||
}
|
||||
|
||||
if loginRespFromEnv.Auth == nil || loginRespFromEnv.Auth.ClientToken == "" {
|
||||
t.Fatalf("no authentication info returned by login with password from env var")
|
||||
}
|
||||
|
||||
// Password fromStr test
|
||||
authFromStr, err := NewLDAPAuth("my-ldap-username", &Password{FromString: allowedPassword})
|
||||
if err != nil {
|
||||
t.Fatalf("error initializing LDAPAuth with password string: %v", err)
|
||||
}
|
||||
|
||||
loginRespFromStr, err := client.Auth().Login(context.TODO(), authFromStr)
|
||||
if err != nil {
|
||||
t.Fatalf("error logging in with string: %v", err)
|
||||
}
|
||||
|
||||
if loginRespFromStr.Auth == nil || loginRespFromStr.Auth.ClientToken == "" {
|
||||
t.Fatalf("no authentication info returned by login with password from string")
|
||||
}
|
||||
|
||||
// Empty User Test
|
||||
_, err = NewLDAPAuth("", &Password{FromString: allowedPassword})
|
||||
if err.Error() != "no user name provided for login" {
|
||||
t.Fatalf("Auth object created for empty username: %v", err)
|
||||
}
|
||||
|
||||
// Empty Password Test
|
||||
_, err = NewLDAPAuth("my-ldap-username", nil)
|
||||
if err.Error() != "no password provided for login" {
|
||||
t.Fatalf("Auth object created when passing a nil Password struct: %v", err)
|
||||
}
|
||||
|
||||
// Auth with Custom MountPath
|
||||
ldapMount := WithMountPath("customMount")
|
||||
_, err = NewLDAPAuth("my-ldap-username", &Password{FromString: allowedPassword}, ldapMount)
|
||||
if err != nil {
|
||||
t.Fatalf("error initializing LDAPAuth with custom mountpath: %v", err)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
module github.com/hashicorp/vault/api/auth/userpass
|
||||
|
||||
go 1.16
|
||||
|
||||
require github.com/hashicorp/vault/api v1.9.2
|
|
@ -0,0 +1,117 @@
|
|||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c=
|
||||
github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
|
||||
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
|
||||
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v0.16.2 h1:K4ev2ib4LdQETX5cSZBG0DVLk1jwGqSPXBjdah3veNs=
|
||||
github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ=
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/vault/api v1.9.2 h1:YjkZLJ7K3inKgMZ0wzCU9OHqc+UqMQyXsPXnf3Cl2as=
|
||||
github.com/hashicorp/vault/api v1.9.2/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -0,0 +1,173 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package userpass
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/vault/api"
|
||||
)
|
||||
|
||||
type UserpassAuth struct {
|
||||
mountPath string
|
||||
username string
|
||||
password string
|
||||
passwordFile string
|
||||
passwordEnv string
|
||||
}
|
||||
|
||||
type Password struct {
|
||||
// Path on the file system where the password corresponding to this
|
||||
// application's Vault role can be found.
|
||||
FromFile string
|
||||
// The name of the environment variable containing the password
|
||||
// that corresponds to this application's Vault role.
|
||||
FromEnv string
|
||||
// The password as a plaintext string value.
|
||||
FromString string
|
||||
}
|
||||
|
||||
var _ api.AuthMethod = (*UserpassAuth)(nil)
|
||||
|
||||
type LoginOption func(a *UserpassAuth) error
|
||||
|
||||
const (
|
||||
defaultMountPath = "userpass"
|
||||
)
|
||||
|
||||
// NewUserpassAuth initializes a new Userpass auth method interface to be
|
||||
// passed as a parameter to the client.Auth().Login method.
|
||||
//
|
||||
// Supported options: WithMountPath
|
||||
func NewUserpassAuth(username string, password *Password, opts ...LoginOption) (*UserpassAuth, error) {
|
||||
if username == "" {
|
||||
return nil, fmt.Errorf("no user name provided for login")
|
||||
}
|
||||
|
||||
if password == nil {
|
||||
return nil, fmt.Errorf("no password provided for login")
|
||||
}
|
||||
|
||||
err := password.validate()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid password: %w", err)
|
||||
}
|
||||
|
||||
a := &UserpassAuth{
|
||||
mountPath: defaultMountPath,
|
||||
username: username,
|
||||
}
|
||||
|
||||
// password will be read in at login time if it comes from a file or environment variable, in case the underlying value changes
|
||||
if password.FromFile != "" {
|
||||
a.passwordFile = password.FromFile
|
||||
}
|
||||
|
||||
if password.FromEnv != "" {
|
||||
a.passwordEnv = password.FromEnv
|
||||
}
|
||||
|
||||
if password.FromString != "" {
|
||||
a.password = password.FromString
|
||||
}
|
||||
|
||||
// Loop through each option
|
||||
for _, opt := range opts {
|
||||
// Call the option giving the instantiated
|
||||
// *UserpassAuth as the argument
|
||||
err := opt(a)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error with login option: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// return the modified auth struct instance
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func (a *UserpassAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, error) {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
loginData := make(map[string]interface{})
|
||||
|
||||
if a.passwordFile != "" {
|
||||
passwordValue, err := a.readPasswordFromFile()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading password: %w", err)
|
||||
}
|
||||
loginData["password"] = passwordValue
|
||||
} else if a.passwordEnv != "" {
|
||||
passwordValue := os.Getenv(a.passwordEnv)
|
||||
if passwordValue == "" {
|
||||
return nil, fmt.Errorf("password was specified with an environment variable with an empty value")
|
||||
}
|
||||
loginData["password"] = passwordValue
|
||||
} else {
|
||||
loginData["password"] = a.password
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("auth/%s/login/%s", a.mountPath, a.username)
|
||||
resp, err := client.Logical().WriteWithContext(ctx, path, loginData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to log in with userpass auth: %w", err)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func WithMountPath(mountPath string) LoginOption {
|
||||
return func(a *UserpassAuth) error {
|
||||
a.mountPath = mountPath
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a *UserpassAuth) readPasswordFromFile() (string, error) {
|
||||
passwordFile, err := os.Open(a.passwordFile)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to open file containing password: %w", err)
|
||||
}
|
||||
defer passwordFile.Close()
|
||||
|
||||
limitedReader := io.LimitReader(passwordFile, 1000)
|
||||
passwordBytes, err := io.ReadAll(limitedReader)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to read password: %w", err)
|
||||
}
|
||||
|
||||
passwordValue := strings.TrimSuffix(string(passwordBytes), "\n")
|
||||
|
||||
return passwordValue, nil
|
||||
}
|
||||
|
||||
func (password *Password) validate() error {
|
||||
if password.FromFile == "" && password.FromEnv == "" && password.FromString == "" {
|
||||
return fmt.Errorf("password for Userpass auth must be provided with a source file, environment variable, or plaintext string")
|
||||
}
|
||||
|
||||
if password.FromFile != "" {
|
||||
if password.FromEnv != "" || password.FromString != "" {
|
||||
return fmt.Errorf("only one source for the password should be specified")
|
||||
}
|
||||
}
|
||||
|
||||
if password.FromEnv != "" {
|
||||
if password.FromFile != "" || password.FromString != "" {
|
||||
return fmt.Errorf("only one source for the password should be specified")
|
||||
}
|
||||
}
|
||||
|
||||
if password.FromString != "" {
|
||||
if password.FromFile != "" || password.FromEnv != "" {
|
||||
return fmt.Errorf("only one source for the password should be specified")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package userpass
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/vault/api"
|
||||
)
|
||||
|
||||
// testHTTPServer creates a test HTTP server that handles requests until
|
||||
// the listener returned is closed.
|
||||
func testHTTPServer(
|
||||
t *testing.T, handler http.Handler,
|
||||
) (*api.Config, net.Listener) {
|
||||
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
server := &http.Server{Handler: handler}
|
||||
go server.Serve(ln)
|
||||
|
||||
config := api.DefaultConfig()
|
||||
config.Address = fmt.Sprintf("http://%s", ln.Addr())
|
||||
|
||||
return config, ln
|
||||
}
|
||||
|
||||
func init() {
|
||||
os.Setenv("VAULT_TOKEN", "")
|
||||
}
|
||||
|
||||
func TestLogin(t *testing.T) {
|
||||
passwordEnvVar := "USERPASS_PASSWORD"
|
||||
allowedPassword := "my-password"
|
||||
|
||||
content := []byte(allowedPassword)
|
||||
tmpfile, err := os.CreateTemp("", "file-containing-password")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating temp file: %v", err)
|
||||
}
|
||||
defer os.Remove(tmpfile.Name()) // clean up
|
||||
err = os.Setenv(passwordEnvVar, allowedPassword)
|
||||
if err != nil {
|
||||
t.Fatalf("error writing password to env var: %v", err)
|
||||
}
|
||||
|
||||
if _, err := tmpfile.Write(content); err != nil {
|
||||
t.Fatalf("error writing to temp file: %v", err)
|
||||
}
|
||||
if err := tmpfile.Close(); err != nil {
|
||||
t.Fatalf("error closing temp file: %v", err)
|
||||
}
|
||||
|
||||
// a response to return if the correct values were passed to login
|
||||
authSecret := &api.Secret{
|
||||
Auth: &api.SecretAuth{
|
||||
ClientToken: "a-client-token",
|
||||
},
|
||||
}
|
||||
|
||||
authBytes, err := json.Marshal(authSecret)
|
||||
if err != nil {
|
||||
t.Fatalf("error marshaling json: %v", err)
|
||||
}
|
||||
|
||||
handler := func(w http.ResponseWriter, req *http.Request) {
|
||||
payload := make(map[string]interface{})
|
||||
err := json.NewDecoder(req.Body).Decode(&payload)
|
||||
if err != nil {
|
||||
t.Fatalf("error decoding json: %v", err)
|
||||
}
|
||||
if payload["password"] == allowedPassword {
|
||||
w.Write(authBytes)
|
||||
}
|
||||
}
|
||||
|
||||
config, ln := testHTTPServer(t, http.HandlerFunc(handler))
|
||||
defer ln.Close()
|
||||
|
||||
config.Address = strings.ReplaceAll(config.Address, "127.0.0.1", "localhost")
|
||||
client, err := api.NewClient(config)
|
||||
if err != nil {
|
||||
t.Fatalf("error initializing Vault client: %v", err)
|
||||
}
|
||||
|
||||
authFromFile, err := NewUserpassAuth("my-role-id", &Password{FromFile: tmpfile.Name()})
|
||||
if err != nil {
|
||||
t.Fatalf("error initializing AppRoleAuth with password file: %v", err)
|
||||
}
|
||||
|
||||
loginRespFromFile, err := client.Auth().Login(context.TODO(), authFromFile)
|
||||
if err != nil {
|
||||
t.Fatalf("error logging in with password from file: %v", err)
|
||||
}
|
||||
if loginRespFromFile.Auth == nil || loginRespFromFile.Auth.ClientToken == "" {
|
||||
t.Fatalf("no authentication info returned by login")
|
||||
}
|
||||
|
||||
authFromEnv, err := NewUserpassAuth("my-role-id", &Password{FromEnv: passwordEnvVar})
|
||||
if err != nil {
|
||||
t.Fatalf("error initializing AppRoleAuth with password env var: %v", err)
|
||||
}
|
||||
|
||||
loginRespFromEnv, err := client.Auth().Login(context.TODO(), authFromEnv)
|
||||
if err != nil {
|
||||
t.Fatalf("error logging in with password from env var: %v", err)
|
||||
}
|
||||
if loginRespFromEnv.Auth == nil || loginRespFromEnv.Auth.ClientToken == "" {
|
||||
t.Fatalf("no authentication info returned by login with password from env var")
|
||||
}
|
||||
|
||||
authFromStr, err := NewUserpassAuth("my-role-id", &Password{FromString: allowedPassword})
|
||||
if err != nil {
|
||||
t.Fatalf("error initializing AppRoleAuth with password string: %v", err)
|
||||
}
|
||||
|
||||
loginRespFromStr, err := client.Auth().Login(context.TODO(), authFromStr)
|
||||
if err != nil {
|
||||
t.Fatalf("error logging in with string: %v", err)
|
||||
}
|
||||
if loginRespFromStr.Auth == nil || loginRespFromStr.Auth.ClientToken == "" {
|
||||
t.Fatalf("no authentication info returned by login with password from string")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type mockAuthMethod struct {
|
||||
mockedSecret *Secret
|
||||
mockedError error
|
||||
}
|
||||
|
||||
func (m *mockAuthMethod) Login(_ context.Context, _ *Client) (*Secret, error) {
|
||||
return m.mockedSecret, m.mockedError
|
||||
}
|
||||
|
||||
func TestAuth_Login(t *testing.T) {
|
||||
a := &Auth{
|
||||
c: &Client{},
|
||||
}
|
||||
|
||||
m := mockAuthMethod{
|
||||
mockedSecret: &Secret{
|
||||
Auth: &SecretAuth{
|
||||
ClientToken: "a-client-token",
|
||||
},
|
||||
},
|
||||
mockedError: nil,
|
||||
}
|
||||
|
||||
t.Run("Login should set token on success", func(t *testing.T) {
|
||||
if a.c.Token() != "" {
|
||||
t.Errorf("client token was %v expected to be unset", a.c.Token())
|
||||
}
|
||||
|
||||
_, err := a.Login(context.Background(), &m)
|
||||
if err != nil {
|
||||
t.Errorf("Login() error = %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if a.c.Token() != m.mockedSecret.Auth.ClientToken {
|
||||
t.Errorf("client token was %v expected %v", a.c.Token(), m.mockedSecret.Auth.ClientToken)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestAuth_MFALoginSinglePhase(t *testing.T) {
|
||||
t.Run("MFALogin() should succeed if credentials are passed in", func(t *testing.T) {
|
||||
a := &Auth{
|
||||
c: &Client{},
|
||||
}
|
||||
|
||||
m := mockAuthMethod{
|
||||
mockedSecret: &Secret{
|
||||
Auth: &SecretAuth{
|
||||
ClientToken: "a-client-token",
|
||||
},
|
||||
},
|
||||
mockedError: nil,
|
||||
}
|
||||
|
||||
_, err := a.MFALogin(context.Background(), &m, "testMethod:testPasscode")
|
||||
if err != nil {
|
||||
t.Errorf("MFALogin() error %v", err)
|
||||
return
|
||||
}
|
||||
if a.c.Token() != m.mockedSecret.Auth.ClientToken {
|
||||
t.Errorf("client token was %v expected %v", a.c.Token(), m.mockedSecret.Auth.ClientToken)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestAuth_MFALoginTwoPhase(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
a *Auth
|
||||
m *mockAuthMethod
|
||||
creds *string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "return MFARequirements",
|
||||
a: &Auth{
|
||||
c: &Client{},
|
||||
},
|
||||
m: &mockAuthMethod{
|
||||
mockedSecret: &Secret{
|
||||
Auth: &SecretAuth{
|
||||
MFARequirement: &MFARequirement{
|
||||
MFARequestID: "a-req-id",
|
||||
MFAConstraints: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "error if no MFARequirements",
|
||||
a: &Auth{
|
||||
c: &Client{},
|
||||
},
|
||||
m: &mockAuthMethod{
|
||||
mockedSecret: &Secret{
|
||||
Auth: &SecretAuth{},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
secret, err := tt.a.MFALogin(context.Background(), tt.m)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("MFALogin() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
if secret.Auth.MFARequirement != tt.m.mockedSecret.Auth.MFARequirement {
|
||||
t.Errorf("MFALogin() returned %v, expected %v", secret.Auth.MFARequirement, tt.m.mockedSecret.Auth.MFARequirement)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,377 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// TokenAuth is used to perform token backend operations on Vault
|
||||
type TokenAuth struct {
|
||||
c *Client
|
||||
}
|
||||
|
||||
// Token is used to return the client for token-backend API calls
|
||||
func (a *Auth) Token() *TokenAuth {
|
||||
return &TokenAuth{c: a.c}
|
||||
}
|
||||
|
||||
func (c *TokenAuth) Create(opts *TokenCreateRequest) (*Secret, error) {
|
||||
return c.CreateWithContext(context.Background(), opts)
|
||||
}
|
||||
|
||||
func (c *TokenAuth) CreateWithContext(ctx context.Context, opts *TokenCreateRequest) (*Secret, error) {
|
||||
ctx, cancelFunc := c.c.withConfiguredTimeout(ctx)
|
||||
defer cancelFunc()
|
||||
|
||||
r := c.c.NewRequest(http.MethodPost, "/v1/auth/token/create")
|
||||
if err := r.SetJSONBody(opts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.c.rawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
func (c *TokenAuth) CreateOrphan(opts *TokenCreateRequest) (*Secret, error) {
|
||||
return c.CreateOrphanWithContext(context.Background(), opts)
|
||||
}
|
||||
|
||||
func (c *TokenAuth) CreateOrphanWithContext(ctx context.Context, opts *TokenCreateRequest) (*Secret, error) {
|
||||
ctx, cancelFunc := c.c.withConfiguredTimeout(ctx)
|
||||
defer cancelFunc()
|
||||
|
||||
r := c.c.NewRequest(http.MethodPost, "/v1/auth/token/create-orphan")
|
||||
if err := r.SetJSONBody(opts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.c.rawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
func (c *TokenAuth) CreateWithRole(opts *TokenCreateRequest, roleName string) (*Secret, error) {
|
||||
return c.CreateWithRoleWithContext(context.Background(), opts, roleName)
|
||||
}
|
||||
|
||||
func (c *TokenAuth) CreateWithRoleWithContext(ctx context.Context, opts *TokenCreateRequest, roleName string) (*Secret, error) {
|
||||
ctx, cancelFunc := c.c.withConfiguredTimeout(ctx)
|
||||
defer cancelFunc()
|
||||
|
||||
r := c.c.NewRequest(http.MethodPost, "/v1/auth/token/create/"+roleName)
|
||||
if err := r.SetJSONBody(opts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.c.rawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
func (c *TokenAuth) Lookup(token string) (*Secret, error) {
|
||||
return c.LookupWithContext(context.Background(), token)
|
||||
}
|
||||
|
||||
func (c *TokenAuth) LookupWithContext(ctx context.Context, token string) (*Secret, error) {
|
||||
ctx, cancelFunc := c.c.withConfiguredTimeout(ctx)
|
||||
defer cancelFunc()
|
||||
|
||||
r := c.c.NewRequest(http.MethodPost, "/v1/auth/token/lookup")
|
||||
if err := r.SetJSONBody(map[string]interface{}{
|
||||
"token": token,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.c.rawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
func (c *TokenAuth) LookupAccessor(accessor string) (*Secret, error) {
|
||||
return c.LookupAccessorWithContext(context.Background(), accessor)
|
||||
}
|
||||
|
||||
func (c *TokenAuth) LookupAccessorWithContext(ctx context.Context, accessor string) (*Secret, error) {
|
||||
ctx, cancelFunc := c.c.withConfiguredTimeout(ctx)
|
||||
defer cancelFunc()
|
||||
|
||||
r := c.c.NewRequest(http.MethodPost, "/v1/auth/token/lookup-accessor")
|
||||
if err := r.SetJSONBody(map[string]interface{}{
|
||||
"accessor": accessor,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.c.rawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
func (c *TokenAuth) LookupSelf() (*Secret, error) {
|
||||
return c.LookupSelfWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (c *TokenAuth) LookupSelfWithContext(ctx context.Context) (*Secret, error) {
|
||||
ctx, cancelFunc := c.c.withConfiguredTimeout(ctx)
|
||||
defer cancelFunc()
|
||||
|
||||
r := c.c.NewRequest(http.MethodGet, "/v1/auth/token/lookup-self")
|
||||
|
||||
resp, err := c.c.rawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
func (c *TokenAuth) RenewAccessor(accessor string, increment int) (*Secret, error) {
|
||||
return c.RenewAccessorWithContext(context.Background(), accessor, increment)
|
||||
}
|
||||
|
||||
func (c *TokenAuth) RenewAccessorWithContext(ctx context.Context, accessor string, increment int) (*Secret, error) {
|
||||
ctx, cancelFunc := c.c.withConfiguredTimeout(ctx)
|
||||
defer cancelFunc()
|
||||
|
||||
r := c.c.NewRequest(http.MethodPost, "/v1/auth/token/renew-accessor")
|
||||
if err := r.SetJSONBody(map[string]interface{}{
|
||||
"accessor": accessor,
|
||||
"increment": increment,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.c.rawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
func (c *TokenAuth) Renew(token string, increment int) (*Secret, error) {
|
||||
return c.RenewWithContext(context.Background(), token, increment)
|
||||
}
|
||||
|
||||
func (c *TokenAuth) RenewWithContext(ctx context.Context, token string, increment int) (*Secret, error) {
|
||||
ctx, cancelFunc := c.c.withConfiguredTimeout(ctx)
|
||||
defer cancelFunc()
|
||||
|
||||
r := c.c.NewRequest(http.MethodPut, "/v1/auth/token/renew")
|
||||
if err := r.SetJSONBody(map[string]interface{}{
|
||||
"token": token,
|
||||
"increment": increment,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.c.rawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
func (c *TokenAuth) RenewSelf(increment int) (*Secret, error) {
|
||||
return c.RenewSelfWithContext(context.Background(), increment)
|
||||
}
|
||||
|
||||
func (c *TokenAuth) RenewSelfWithContext(ctx context.Context, increment int) (*Secret, error) {
|
||||
ctx, cancelFunc := c.c.withConfiguredTimeout(ctx)
|
||||
defer cancelFunc()
|
||||
|
||||
r := c.c.NewRequest(http.MethodPut, "/v1/auth/token/renew-self")
|
||||
|
||||
body := map[string]interface{}{"increment": increment}
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.c.rawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
// RenewTokenAsSelf wraps RenewTokenAsSelfWithContext using context.Background.
|
||||
func (c *TokenAuth) RenewTokenAsSelf(token string, increment int) (*Secret, error) {
|
||||
return c.RenewTokenAsSelfWithContext(context.Background(), token, increment)
|
||||
}
|
||||
|
||||
// RenewTokenAsSelfWithContext behaves like renew-self, but authenticates using a provided
|
||||
// token instead of the token attached to the client.
|
||||
func (c *TokenAuth) RenewTokenAsSelfWithContext(ctx context.Context, token string, increment int) (*Secret, error) {
|
||||
ctx, cancelFunc := c.c.withConfiguredTimeout(ctx)
|
||||
defer cancelFunc()
|
||||
|
||||
r := c.c.NewRequest(http.MethodPut, "/v1/auth/token/renew-self")
|
||||
r.ClientToken = token
|
||||
|
||||
body := map[string]interface{}{"increment": increment}
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.c.rawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
// RevokeAccessor wraps RevokeAccessorWithContext using context.Background.
|
||||
func (c *TokenAuth) RevokeAccessor(accessor string) error {
|
||||
return c.RevokeAccessorWithContext(context.Background(), accessor)
|
||||
}
|
||||
|
||||
// RevokeAccessorWithContext revokes a token associated with the given accessor
|
||||
// along with all the child tokens.
|
||||
func (c *TokenAuth) RevokeAccessorWithContext(ctx context.Context, accessor string) error {
|
||||
ctx, cancelFunc := c.c.withConfiguredTimeout(ctx)
|
||||
defer cancelFunc()
|
||||
|
||||
r := c.c.NewRequest(http.MethodPost, "/v1/auth/token/revoke-accessor")
|
||||
if err := r.SetJSONBody(map[string]interface{}{
|
||||
"accessor": accessor,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := c.c.rawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RevokeOrphan wraps RevokeOrphanWithContext using context.Background.
|
||||
func (c *TokenAuth) RevokeOrphan(token string) error {
|
||||
return c.RevokeOrphanWithContext(context.Background(), token)
|
||||
}
|
||||
|
||||
// RevokeOrphanWithContext revokes a token without revoking the tree underneath it (so
|
||||
// child tokens are orphaned rather than revoked)
|
||||
func (c *TokenAuth) RevokeOrphanWithContext(ctx context.Context, token string) error {
|
||||
ctx, cancelFunc := c.c.withConfiguredTimeout(ctx)
|
||||
defer cancelFunc()
|
||||
|
||||
r := c.c.NewRequest(http.MethodPut, "/v1/auth/token/revoke-orphan")
|
||||
if err := r.SetJSONBody(map[string]interface{}{
|
||||
"token": token,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := c.c.rawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RevokeSelf wraps RevokeSelfWithContext using context.Background.
|
||||
func (c *TokenAuth) RevokeSelf(token string) error {
|
||||
return c.RevokeSelfWithContext(context.Background(), token)
|
||||
}
|
||||
|
||||
// RevokeSelfWithContext revokes the token making the call. The `token` parameter is kept
|
||||
// for backwards compatibility but is ignored; only the client's set token has
|
||||
// an effect.
|
||||
func (c *TokenAuth) RevokeSelfWithContext(ctx context.Context, token string) error {
|
||||
ctx, cancelFunc := c.c.withConfiguredTimeout(ctx)
|
||||
defer cancelFunc()
|
||||
|
||||
r := c.c.NewRequest(http.MethodPut, "/v1/auth/token/revoke-self")
|
||||
|
||||
resp, err := c.c.rawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RevokeTree wraps RevokeTreeWithContext using context.Background.
|
||||
func (c *TokenAuth) RevokeTree(token string) error {
|
||||
return c.RevokeTreeWithContext(context.Background(), token)
|
||||
}
|
||||
|
||||
// RevokeTreeWithContext is the "normal" revoke operation that revokes the given token and
|
||||
// the entire tree underneath -- all of its child tokens, their child tokens,
|
||||
// etc.
|
||||
func (c *TokenAuth) RevokeTreeWithContext(ctx context.Context, token string) error {
|
||||
ctx, cancelFunc := c.c.withConfiguredTimeout(ctx)
|
||||
defer cancelFunc()
|
||||
|
||||
r := c.c.NewRequest(http.MethodPut, "/v1/auth/token/revoke")
|
||||
if err := r.SetJSONBody(map[string]interface{}{
|
||||
"token": token,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := c.c.rawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TokenCreateRequest is the options structure for creating a token.
|
||||
type TokenCreateRequest struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Policies []string `json:"policies,omitempty"`
|
||||
Metadata map[string]string `json:"meta,omitempty"`
|
||||
Lease string `json:"lease,omitempty"`
|
||||
TTL string `json:"ttl,omitempty"`
|
||||
ExplicitMaxTTL string `json:"explicit_max_ttl,omitempty"`
|
||||
Period string `json:"period,omitempty"`
|
||||
NoParent bool `json:"no_parent,omitempty"`
|
||||
NoDefaultPolicy bool `json:"no_default_policy,omitempty"`
|
||||
DisplayName string `json:"display_name"`
|
||||
NumUses int `json:"num_uses"`
|
||||
Renewable *bool `json:"renewable,omitempty"`
|
||||
Type string `json:"type"`
|
||||
EntityAlias string `json:"entity_alias"`
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,39 @@
|
|||
module github.com/hashicorp/vault/api
|
||||
|
||||
// The Go version directive for the api package should normally only be updated when
|
||||
// code in the api package requires a newer Go version to build. It should not
|
||||
// automatically track the Go version used to build Vault itself. Many projects import
|
||||
// the api module and we don't want to impose a newer version on them any more than we
|
||||
// have to.
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/cenkalti/backoff/v3 v3.0.0
|
||||
github.com/go-jose/go-jose/v3 v3.0.0
|
||||
github.com/go-test/deep v1.0.2
|
||||
github.com/hashicorp/errwrap v1.1.0
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2
|
||||
github.com/hashicorp/go-hclog v0.16.2
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6
|
||||
github.com/hashicorp/go-rootcerts v1.0.2
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2
|
||||
github.com/hashicorp/hcl v1.0.0
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
golang.org/x/net v0.7.0
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/fatih/color v1.7.0 // indirect
|
||||
github.com/google/go-cmp v0.5.7 // indirect
|
||||
github.com/hashicorp/go-sockaddr v1.0.2 // indirect
|
||||
github.com/mattn/go-colorable v0.1.6 // indirect
|
||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/ryanuber/go-glob v1.0.0 // indirect
|
||||
golang.org/x/crypto v0.6.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
)
|
|
@ -0,0 +1,93 @@
|
|||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c=
|
||||
github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
|
||||
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
|
||||
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v0.16.2 h1:K4ev2ib4LdQETX5cSZBG0DVLk1jwGqSPXBjdah3veNs=
|
||||
github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ=
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Help wraps HelpWithContext using context.Background.
|
||||
func (c *Client) Help(path string) (*Help, error) {
|
||||
return c.HelpWithContext(context.Background(), path)
|
||||
}
|
||||
|
||||
// HelpWithContext reads the help information for the given path.
|
||||
func (c *Client) HelpWithContext(ctx context.Context, path string) (*Help, error) {
|
||||
ctx, cancelFunc := c.withConfiguredTimeout(ctx)
|
||||
defer cancelFunc()
|
||||
|
||||
r := c.NewRequest(http.MethodGet, fmt.Sprintf("/v1/%s", path))
|
||||
r.Params.Add("help", "1")
|
||||
|
||||
resp, err := c.rawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result Help
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
type Help struct {
|
||||
Help string `json:"help"`
|
||||
SeeAlso []string `json:"see_also"`
|
||||
OpenAPI map[string]interface{} `json:"openapi"`
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package api
|
||||
|
||||
import "errors"
|
||||
|
||||
// ErrSecretNotFound is returned by KVv1 and KVv2 wrappers to indicate that the
|
||||
// secret is missing at the given location.
|
||||
var ErrSecretNotFound = errors.New("secret not found")
|
||||
|
||||
// A KVSecret is a key-value secret returned by Vault's KV secrets engine,
|
||||
// and is the most basic type of secret stored in Vault.
|
||||
//
|
||||
// Data contains the key-value pairs of the secret itself,
|
||||
// while Metadata contains a subset of metadata describing
|
||||
// this particular version of the secret.
|
||||
// The Metadata field for a KV v1 secret will always be nil, as
|
||||
// metadata is only supported starting in KV v2.
|
||||
//
|
||||
// The Raw field can be inspected for information about the lease,
|
||||
// and passed to a LifetimeWatcher object for periodic renewal.
|
||||
type KVSecret struct {
|
||||
Data map[string]interface{}
|
||||
VersionMetadata *KVVersionMetadata
|
||||
CustomMetadata map[string]interface{}
|
||||
Raw *Secret
|
||||
}
|
||||
|
||||
// KVv1 is used to return a client for reads and writes against
|
||||
// a KV v1 secrets engine in Vault.
|
||||
//
|
||||
// The mount path is the location where the target KV secrets engine resides
|
||||
// in Vault.
|
||||
//
|
||||
// While v1 is not necessarily deprecated, Vault development servers tend to
|
||||
// use v2 as the version of the KV secrets engine, as this is what's mounted
|
||||
// by default when a server is started in -dev mode. See the kvv2 struct.
|
||||
//
|
||||
// Learn more about the KV secrets engine here:
|
||||
// https://www.vaultproject.io/docs/secrets/kv
|
||||
func (c *Client) KVv1(mountPath string) *KVv1 {
|
||||
return &KVv1{c: c, mountPath: mountPath}
|
||||
}
|
||||
|
||||
// KVv2 is used to return a client for reads and writes against
|
||||
// a KV v2 secrets engine in Vault.
|
||||
//
|
||||
// The mount path is the location where the target KV secrets engine resides
|
||||
// in Vault.
|
||||
//
|
||||
// Vault development servers tend to have "secret" as the mount path,
|
||||
// as these are the default settings when a server is started in -dev mode.
|
||||
//
|
||||
// Learn more about the KV secrets engine here:
|
||||
// https://www.vaultproject.io/docs/secrets/kv
|
||||
func (c *Client) KVv2(mountPath string) *KVv2 {
|
||||
return &KVv2{c: c, mountPath: mountPath}
|
||||
}
|
|
@ -0,0 +1,390 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestExtractVersionMetadata(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
inputCreatedTimeStr := "2022-05-06T23:02:04.865025Z"
|
||||
inputDeletionTimeStr := "2022-06-17T01:15:03.279013Z"
|
||||
expectedCreatedTimeParsed, err := time.Parse(time.RFC3339, inputCreatedTimeStr)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to parse expected created time: %v", err)
|
||||
}
|
||||
expectedDeletionTimeParsed, err := time.Parse(time.RFC3339, inputDeletionTimeStr)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to parse expected created time: %v", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
input *Secret
|
||||
expected *KVVersionMetadata
|
||||
}{
|
||||
{
|
||||
name: "a secret",
|
||||
input: &Secret{
|
||||
Data: map[string]interface{}{
|
||||
"data": map[string]interface{}{
|
||||
"password": "Hashi123",
|
||||
},
|
||||
"metadata": map[string]interface{}{
|
||||
"version": 10,
|
||||
"created_time": inputCreatedTimeStr,
|
||||
"deletion_time": "",
|
||||
"destroyed": false,
|
||||
"custom_metadata": nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &KVVersionMetadata{
|
||||
Version: 10,
|
||||
CreatedTime: expectedCreatedTimeParsed,
|
||||
DeletionTime: time.Time{},
|
||||
Destroyed: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "a secret that has been deleted",
|
||||
input: &Secret{
|
||||
Data: map[string]interface{}{
|
||||
"data": map[string]interface{}{
|
||||
"password": "Hashi123",
|
||||
},
|
||||
"metadata": map[string]interface{}{
|
||||
"version": 10,
|
||||
"created_time": inputCreatedTimeStr,
|
||||
"deletion_time": inputDeletionTimeStr,
|
||||
"destroyed": false,
|
||||
"custom_metadata": nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &KVVersionMetadata{
|
||||
Version: 10,
|
||||
CreatedTime: expectedCreatedTimeParsed,
|
||||
DeletionTime: expectedDeletionTimeParsed,
|
||||
Destroyed: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "a response from a Write operation",
|
||||
input: &Secret{
|
||||
Data: map[string]interface{}{
|
||||
"version": 10,
|
||||
"created_time": inputCreatedTimeStr,
|
||||
"deletion_time": "",
|
||||
"destroyed": false,
|
||||
"custom_metadata": nil,
|
||||
},
|
||||
},
|
||||
expected: &KVVersionMetadata{
|
||||
Version: 10,
|
||||
CreatedTime: expectedCreatedTimeParsed,
|
||||
DeletionTime: time.Time{},
|
||||
Destroyed: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
versionMetadata, err := extractVersionMetadata(tc.input)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(versionMetadata, tc.expected) {
|
||||
t.Fatalf("%s: got\n%#v\nexpected\n%#v\n", tc.name, versionMetadata, tc.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractDataAndVersionMetadata(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
inputCreatedTimeStr := "2022-05-06T23:02:04.865025Z"
|
||||
inputDeletionTimeStr := "2022-06-17T01:15:03.279013Z"
|
||||
expectedCreatedTimeParsed, err := time.Parse(time.RFC3339, inputCreatedTimeStr)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to parse expected created time: %v", err)
|
||||
}
|
||||
expectedDeletionTimeParsed, err := time.Parse(time.RFC3339, inputDeletionTimeStr)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to parse expected created time: %v", err)
|
||||
}
|
||||
|
||||
readResp := &Secret{
|
||||
Data: map[string]interface{}{
|
||||
"data": map[string]interface{}{
|
||||
"password": "Hashi123",
|
||||
},
|
||||
"metadata": map[string]interface{}{
|
||||
"version": 10,
|
||||
"created_time": inputCreatedTimeStr,
|
||||
"deletion_time": "",
|
||||
"destroyed": false,
|
||||
"custom_metadata": nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
readRespDeleted := &Secret{
|
||||
Data: map[string]interface{}{
|
||||
"data": nil,
|
||||
"metadata": map[string]interface{}{
|
||||
"version": 10,
|
||||
"created_time": inputCreatedTimeStr,
|
||||
"deletion_time": inputDeletionTimeStr,
|
||||
"destroyed": false,
|
||||
"custom_metadata": nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
input *Secret
|
||||
expected *KVSecret
|
||||
}{
|
||||
{
|
||||
name: "a response from a Read operation",
|
||||
input: readResp,
|
||||
expected: &KVSecret{
|
||||
Data: map[string]interface{}{
|
||||
"password": "Hashi123",
|
||||
},
|
||||
VersionMetadata: &KVVersionMetadata{
|
||||
Version: 10,
|
||||
CreatedTime: expectedCreatedTimeParsed,
|
||||
DeletionTime: time.Time{},
|
||||
Destroyed: false,
|
||||
},
|
||||
// it's tempting to test some Secrets with custom_metadata but
|
||||
// we can't in this test because it isn't until we call the
|
||||
// extractCustomMetadata function that the custom metadata
|
||||
// gets added onto the struct. See TestExtractCustomMetadata.
|
||||
CustomMetadata: nil,
|
||||
Raw: readResp,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "a secret that has been deleted and thus has nil data",
|
||||
input: readRespDeleted,
|
||||
expected: &KVSecret{
|
||||
Data: nil,
|
||||
VersionMetadata: &KVVersionMetadata{
|
||||
Version: 10,
|
||||
CreatedTime: expectedCreatedTimeParsed,
|
||||
DeletionTime: expectedDeletionTimeParsed,
|
||||
Destroyed: false,
|
||||
},
|
||||
CustomMetadata: nil,
|
||||
Raw: readRespDeleted,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
dvm, err := extractDataAndVersionMetadata(tc.input)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(dvm, tc.expected) {
|
||||
t.Fatalf("%s: got\n%#v\nexpected\n%#v\n", tc.name, dvm, tc.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractFullMetadata(t *testing.T) {
|
||||
inputCreatedTimeStr := "2022-05-20T00:51:49.419794Z"
|
||||
expectedCreatedTimeParsed, err := time.Parse(time.RFC3339, inputCreatedTimeStr)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to parse expected created time: %v", err)
|
||||
}
|
||||
|
||||
inputUpdatedTimeStr := "2022-05-20T20:23:43.284488Z"
|
||||
expectedUpdatedTimeParsed, err := time.Parse(time.RFC3339, inputUpdatedTimeStr)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to parse expected updated time: %v", err)
|
||||
}
|
||||
|
||||
inputDeletedTimeStr := "2022-05-21T00:05:49.521697Z"
|
||||
expectedDeletedTimeParsed, err := time.Parse(time.RFC3339, inputDeletedTimeStr)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to parse expected deletion time: %v", err)
|
||||
}
|
||||
|
||||
metadataResp := &Secret{
|
||||
Data: map[string]interface{}{
|
||||
"cas_required": true,
|
||||
"created_time": inputCreatedTimeStr,
|
||||
"current_version": 2,
|
||||
"custom_metadata": map[string]interface{}{
|
||||
"org": "eng",
|
||||
},
|
||||
"delete_version_after": "200s",
|
||||
"max_versions": 3,
|
||||
"oldest_version": 1,
|
||||
"updated_time": inputUpdatedTimeStr,
|
||||
"versions": map[string]interface{}{
|
||||
"2": map[string]interface{}{
|
||||
"created_time": inputUpdatedTimeStr,
|
||||
"deletion_time": "",
|
||||
"destroyed": false,
|
||||
},
|
||||
"1": map[string]interface{}{
|
||||
"created_time": inputCreatedTimeStr,
|
||||
"deletion_time": inputDeletedTimeStr,
|
||||
"destroyed": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
input *Secret
|
||||
expected *KVMetadata
|
||||
}{
|
||||
{
|
||||
name: "a metadata response",
|
||||
input: metadataResp,
|
||||
expected: &KVMetadata{
|
||||
CASRequired: true,
|
||||
CreatedTime: expectedCreatedTimeParsed,
|
||||
CurrentVersion: 2,
|
||||
CustomMetadata: map[string]interface{}{
|
||||
"org": "eng",
|
||||
},
|
||||
DeleteVersionAfter: time.Duration(200 * time.Second),
|
||||
MaxVersions: 3,
|
||||
OldestVersion: 1,
|
||||
UpdatedTime: expectedUpdatedTimeParsed,
|
||||
Versions: map[string]KVVersionMetadata{
|
||||
"2": {
|
||||
Version: 2,
|
||||
CreatedTime: expectedUpdatedTimeParsed,
|
||||
DeletionTime: time.Time{},
|
||||
},
|
||||
"1": {
|
||||
Version: 1,
|
||||
CreatedTime: expectedCreatedTimeParsed,
|
||||
DeletionTime: expectedDeletedTimeParsed,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
md, err := extractFullMetadata(tc.input)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(md, tc.expected) {
|
||||
t.Fatalf("%s: got\n%#v\nexpected\n%#v\n", tc.name, md, tc.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractCustomMetadata(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
inputAPIResp *Secret
|
||||
expected map[string]interface{}
|
||||
}{
|
||||
{
|
||||
name: "a read response with some custom metadata",
|
||||
inputAPIResp: &Secret{
|
||||
Data: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"custom_metadata": map[string]interface{}{"org": "eng"},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{"org": "eng"},
|
||||
},
|
||||
{
|
||||
name: "a write response with some (pre-existing) custom metadata",
|
||||
inputAPIResp: &Secret{
|
||||
Data: map[string]interface{}{
|
||||
"custom_metadata": map[string]interface{}{"org": "eng"},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{"org": "eng"},
|
||||
},
|
||||
{
|
||||
name: "a read response with no custom metadata from a pre-1.9 Vault server",
|
||||
inputAPIResp: &Secret{
|
||||
Data: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}(nil),
|
||||
},
|
||||
{
|
||||
name: "a write response with no custom metadata from a pre-1.9 Vault server",
|
||||
inputAPIResp: &Secret{
|
||||
Data: map[string]interface{}{},
|
||||
},
|
||||
expected: map[string]interface{}(nil),
|
||||
},
|
||||
{
|
||||
name: "a read response with no custom metadata from a post-1.9 Vault server",
|
||||
inputAPIResp: &Secret{
|
||||
Data: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"custom_metadata": nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}(nil),
|
||||
},
|
||||
{
|
||||
name: "a write response with no custom metadata from a post-1.9 Vault server",
|
||||
inputAPIResp: &Secret{
|
||||
Data: map[string]interface{}{
|
||||
"custom_metadata": nil,
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}(nil),
|
||||
},
|
||||
{
|
||||
name: "a read response where custom metadata was deleted",
|
||||
inputAPIResp: &Secret{
|
||||
Data: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"custom_metadata": map[string]interface{}{},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{},
|
||||
},
|
||||
{
|
||||
name: "a write response where custom metadata was deleted",
|
||||
inputAPIResp: &Secret{
|
||||
Data: map[string]interface{}{
|
||||
"custom_metadata": map[string]interface{}{},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
cm := extractCustomMetadata(tc.inputAPIResp)
|
||||
|
||||
if !reflect.DeepEqual(cm, tc.expected) {
|
||||
t.Fatalf("%s: got\n%#v\nexpected\n%#v\n", tc.name, cm, tc.expected)
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue