Compare commits

..

2 Commits
main ... v1.8.0

Author SHA1 Message Date
Marcos Yacob 872f76d4e1
Update changelog date to 09-20-2024 (#4504)
Signed-off-by: Marcos Yacob <marcos.yacob@hpe.com>
2023-09-20 13:41:07 -03:00
Ryan Turner 8535a1a19f
v1.8.0 CHANGELOG (#4497)
* v1.8.0 CHANGELOG

Signed-off-by: Ryan Turner <turner@uber.com>
2023-09-20 10:41:56 -03:00
1225 changed files with 22568 additions and 63449 deletions

View File

@ -1,8 +0,0 @@
toplevel="$(git rev-parse --show-toplevel)"
# build and use the managed go sdk
unset GOROOT
make -C "$toplevel" go-check
PATH="$(make --no-print-directory -C "$toplevel" go-bin-path)"
# add custom direnv initialization below here

26
.githooks/pre-commit Executable file
View File

@ -0,0 +1,26 @@
#!/bin/sh
# Copyright 2012 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
# git gofmt pre-commit hook
#
# To use, store as .git/hooks/pre-commit inside your repository and make sure
# it has execute permissions.
#
# This script does not handle file names that contain spaces.
gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '\.go$')
[ -z "$gofiles" ] && exit 0
unformatted=$(gofmt -l $gofiles)
[ -z "$unformatted" ] && exit 0
# Some files are not gofmt'd. Print message and fail.
echo >&2 "Go files must be formatted with gofmt. Please run:"
for fn in $unformatted; do
echo >&2 " gofmt -w $PWD/$fn"
done
exit 1

View File

@ -7,24 +7,18 @@ updates:
time: "09:00"
timezone: "America/Los_Angeles"
groups:
actions:
patterns:
- "github.com/actions/*"
aws-sdk:
patterns:
- "github.com/aws/aws-sdk-go-v2/*"
azure-sdk:
patterns:
- "github.com/Azure/azure-sdk-for-go/*"
google-cloud-sdk:
patterns:
- "cloud.google.com/go/*"
k8s.io:
patterns:
- "k8s.io/*"
sigs.k8s.io:
patterns:
- "sig.k8s.io/*"
azure-sdk:
patterns:
- "github.com/Azure/azure-sdk-for-go/*"
ignore:
- dependency-name: "github.com/spiffe/spire-api-sdk"
- dependency-name: "github.com/spiffe/spire-plugin-sdk"

View File

@ -1,23 +0,0 @@
name: DCO
on:
pull_request: {}
workflow_dispatch: {}
merge_group:
types:
- checks_requested
jobs:
check-dco:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Python 3.x
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
with:
python-version: '3.x'
- name: Check DCO
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
pip3 install -U dco-check
dco-check --exclude-pattern 'dependabot\[bot\]@users\.noreply\.github\.com'

View File

@ -10,6 +10,6 @@ jobs:
steps:
- name: 'Checkout Repository'
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: 'Dependency Review'
uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4.5.0
uses: actions/dependency-review-action@6c5ccdad469c9f8a2996bfecaec55a631a347034 # v3.1.0

View File

@ -10,26 +10,29 @@ env:
jobs:
build-and-publish-images:
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
permissions:
contents: read
id-token: write
packages: write
env:
COSIGN_EXPERIMENTAL: 1
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: Install cosign
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
uses: sigstore/cosign-installer@11086d25041f77fe8fe7b9ea4e48e3b9192b8f19 # v3.1.2
with:
cosign-release: v2.2.3
cosign-release: v1.13.1
- name: Install regctl
uses: regclient/actions/regctl-installer@ce5fd131e371ffcdd7508b478cb223b3511a9183 # main
uses: regclient/actions/regctl-installer@b6614f5f56245066b533343a85f4109bdc38c8cc # main
- name: Build images
run: make images
- name: Log in to GHCR
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
with:
registry: ghcr.io
username: ${{ github.actor }}

View File

@ -2,16 +2,15 @@ name: PR Build
on:
pull_request: {}
workflow_dispatch: {}
merge_group:
types:
- checks_requested
env:
GO_VERSION: 1.21.1
permissions:
contents: read
jobs:
cache-deps:
name: cache-deps (linux)
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
timeout-minutes: 30
permissions:
@ -19,13 +18,13 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: Setup go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: 'go.mod'
go-version: ${{ env.GO_VERSION }}
- name: Setup dep cache
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
@ -34,7 +33,7 @@ jobs:
lint:
name: lint (linux)
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
needs: cache-deps
timeout-minutes: 30
@ -43,18 +42,18 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: Setup go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: 'go.mod'
go-version: ${{ env.GO_VERSION }}
- name: Load cached deps
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
- name: Setup build tool cache
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: .build
key: ${{ runner.os }}-tools-${{ hashFiles('.go-version','Makefile') }}
@ -70,7 +69,7 @@ jobs:
unit-test:
strategy:
matrix:
OS: [ubuntu-22.04, macos-latest]
OS: [ubuntu-20.04, macos-latest]
runs-on: ${{ matrix.OS }}
needs: cache-deps
timeout-minutes: 30
@ -80,13 +79,13 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: Setup go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: 'go.mod'
go-version: ${{ env.GO_VERSION }}
- name: Load cached deps
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
@ -95,7 +94,7 @@ jobs:
unit-test-race-detector:
name: unit-test (linux with race detection)
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
needs: cache-deps
timeout-minutes: 30
@ -104,13 +103,13 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: Setup go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: 'go.mod'
go-version: ${{ env.GO_VERSION }}
- name: Load cached deps
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
@ -119,7 +118,7 @@ jobs:
artifacts:
name: artifacts (linux)
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
needs: [cache-deps, images]
timeout-minutes: 30
@ -128,15 +127,15 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: Setup go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: 'go.mod'
go-version: ${{ env.GO_VERSION }}
- name: Install regctl
uses: regclient/actions/regctl-installer@ce5fd131e371ffcdd7508b478cb223b3511a9183 # main
uses: regclient/actions/regctl-installer@b6614f5f56245066b533343a85f4109bdc38c8cc # main
- name: Download archived images
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
with:
name: images
path: .
@ -146,14 +145,14 @@ jobs:
- name: Build artifacts
run: ./.github/workflows/scripts/build_artifacts.sh ${{ runner.os }}
- name: Archive artifacts
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3
with:
name: binaries-linux
name: binaries
path: ./artifacts/
images:
name: images (linux)
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
needs: [cache-deps]
timeout-minutes: 30
@ -162,31 +161,31 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: Setup go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: 'go.mod'
go-version: ${{ env.GO_VERSION }}
- name: Load cached deps
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
- name: Load cached build tools
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: .build
key: ${{ runner.os }}-tools-${{ hashFiles('.go-version','Makefile') }}
- name: Set up QEMU
uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0
uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
- name: Build images
run: make images-no-load
- name: Export images
run: tar -czvf images.tar.gz *-image.tar
- name: Archive images
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3
with:
name: images
path: images.tar.gz
@ -194,7 +193,7 @@ jobs:
images-windows:
name: images (windows)
runs-on: windows-2022
needs: artifacts-windows
needs: artifact-windows
timeout-minutes: 45
permissions:
@ -202,12 +201,12 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Load cached executables
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: Download artifacts
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
with:
name: bin-windows
path: ./bin/
key: ${{ runner.os }}-executables-${{ github.sha }}
- name: Build images
run: make images-windows
- name: Export images
@ -215,20 +214,20 @@ jobs:
docker save spire-server-windows:latest-local spire-agent-windows:latest-local oidc-discovery-provider-windows:latest-local -o images-windows.tar
gzip images-windows.tar
- name: Archive images
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3
with:
name: images-windows
path: images-windows.tar.gz
build-matrix:
name: Build matrix
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
needs: [cache-deps]
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- id: set-matrix
name: Collect versions
run: |
@ -239,9 +238,10 @@ jobs:
outputs:
test: ${{ steps.set-matrix.outputs.test }}
integration:
name: integration (${{ matrix.arch }}) (${{ strategy.job-index}}/${{ strategy.job-total }})
runs-on: ${{ matrix.runs-on }}
name: integration (linux)
runs-on: ubuntu-20.04
needs: [cache-deps, images]
timeout-minutes: 45
@ -251,17 +251,11 @@ jobs:
strategy:
fail-fast: false
matrix:
arch: [x64, arm64]
num_runners: [10]
runner_id: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
include:
- arch: x64
runs-on: ubuntu-22.04
- arch: arm64
runs-on: ubuntu-22.04-arm
num_runners: [5]
runner_id: [1, 2, 3, 4, 5]
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
with:
# The "upgrade" integration test needs the history to ensure
# that the version number in the source code has been bumped as
@ -269,23 +263,23 @@ jobs:
# fetch depth of zero.
fetch-depth: 0
- name: Setup go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: 'go.mod'
go-version: ${{ env.GO_VERSION }}
- name: Install regctl
uses: regclient/actions/regctl-installer@ce5fd131e371ffcdd7508b478cb223b3511a9183 # main
uses: regclient/actions/regctl-installer@b6614f5f56245066b533343a85f4109bdc38c8cc # main
- name: Load cached deps
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
- name: Load cached build tools
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: .build
key: ${{ runner.os }}-tools-${{ hashFiles('.go-version','Makefile') }}
- name: Download archived images
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
with:
name: images
path: .
@ -299,12 +293,13 @@ jobs:
THIS_RUNNER: ${{ matrix.runner_id }}
TERM: dumb
CICD_TARGET_BRANCH: ${{ github.event.pull_request.base.ref }}
IGNORE_SUITES: ${{ matrix.arch == 'arm64' && 'suites/upstream-authority-ejbca' || '' }} # Waiting for EJBCA to support arm64 (https://github.com/spiffe/spire/issues/6060)
run: ./.github/workflows/scripts/split.sh | xargs ./test/integration/test.sh
integration-k8s:
name: integration-k8s-${{ matrix.test[0] }}-${{ matrix.arch }}
runs-on: ${{ matrix.runs-on }}
name: integration-k8s
runs-on: ubuntu-20.04
needs: [cache-deps, images, build-matrix]
timeout-minutes: 45
@ -314,21 +309,13 @@ jobs:
strategy:
fail-fast: false
matrix:
arch: [x64, arm64]
include:
- arch: x64
runs-on: ubuntu-22.04
num_runners: 1
runner_id: 1
- arch: arm64
runs-on: ubuntu-22.04-arm
num_runners: 1
runner_id: 1
#Test elements should be added as [KubeCTLVersion, K8s-image, KindVersion]
test: ${{ fromJson(needs.build-matrix.outputs.test) }}
num_runners: [1]
runner_id: [1]
#Test elements should be added as [KubeCTLVersion, K8s-image, KindVersion]
test: ${{ fromJson(needs.build-matrix.outputs.test) }}
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
with:
# The "upgrade" integration test needs the history to ensure
# that the version number in the source code has been bumped as
@ -336,23 +323,23 @@ jobs:
# fetch depth of zero.
fetch-depth: 0
- name: Setup go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: 'go.mod'
go-version: ${{ env.GO_VERSION }}
- name: Install regctl
uses: regclient/actions/regctl-installer@ce5fd131e371ffcdd7508b478cb223b3511a9183 # main
uses: regclient/actions/regctl-installer@b6614f5f56245066b533343a85f4109bdc38c8cc # main
- name: Load cached deps
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
- name: Load cached build tools
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: .build
key: ${{ runner.os }}-tools-${{ hashFiles('.go-version','Makefile') }}
- name: Download archived images
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
with:
name: images
path: .
@ -378,11 +365,6 @@ jobs:
needs: images-windows
timeout-minutes: 45
env:
GOPATH: 'D:\golang\go'
GOCACHE: 'D:\golang\cache'
GOMODCACHE: 'D:\golang\modcache'
permissions:
contents: read
@ -391,24 +373,23 @@ jobs:
shell: msys2 {0}
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: Setup go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: 'go.mod'
cache: true
go-version: ${{ env.GO_VERSION }}
- name: Load cached deps
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
- name: Load cached build tools
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: .build
key: ${{ runner.os }}-tools-${{ hashFiles('.go-version','Makefile') }}
- name: Install msys2
uses: msys2/setup-msys2@d44ca8e88d8b43d56cf5670f91747359d5537f97 # v2.26.0
uses: msys2/setup-msys2@07aeda7763550b267746a772dcea5e5ac3340b36 # v2
with:
msystem: MINGW64
update: true
@ -416,7 +397,7 @@ jobs:
install: >-
git base-devel mingw-w64-x86_64-toolchain unzip
- name: Download archived images
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
with:
name: images-windows
path: .
@ -431,24 +412,18 @@ jobs:
runs-on: windows-2022
timeout-minutes: 45
env:
GOPATH: 'D:\golang\go'
GOCACHE: 'D:\golang\cache'
GOMODCACHE: 'D:\golang\modcache'
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: Setup go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: 'go.mod'
cache: true
go-version: ${{ env.GO_VERSION }}
- name: Setup dep cache
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
@ -461,11 +436,6 @@ jobs:
needs: cache-deps-windows
timeout-minutes: 45
env:
GOPATH: 'D:\golang\go'
GOCACHE: 'D:\golang\cache'
GOMODCACHE: 'D:\golang\modcache'
permissions:
contents: read
@ -474,24 +444,23 @@ jobs:
shell: msys2 {0}
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: Setup go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: 'go.mod'
cache: true
go-version: ${{ env.GO_VERSION }}
- name: Load cached deps
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
- name: Setup build tool cache
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: .build
key: ${{ runner.os }}-tools-${{ hashFiles('.go-version','Makefile') }}
- name: Install msys2
uses: msys2/setup-msys2@d44ca8e88d8b43d56cf5670f91747359d5537f97 # v2.26.0
uses: msys2/setup-msys2@07aeda7763550b267746a772dcea5e5ac3340b36 # v2
with:
msystem: MINGW64
update: true
@ -510,11 +479,6 @@ jobs:
needs: cache-deps-windows
timeout-minutes: 45
env:
GOPATH: 'D:\golang\go'
GOCACHE: 'D:\golang\cache'
GOMODCACHE: 'D:\golang\modcache'
permissions:
contents: read
@ -523,19 +487,18 @@ jobs:
shell: msys2 {0}
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: Setup go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: 'go.mod'
cache: true
go-version: ${{ env.GO_VERSION }}
- name: Load cached deps
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
- name: Install msys2
uses: msys2/setup-msys2@d44ca8e88d8b43d56cf5670f91747359d5537f97 # v2.26.0
uses: msys2/setup-msys2@07aeda7763550b267746a772dcea5e5ac3340b36 # v2
with:
msystem: MINGW64
update: true
@ -544,17 +507,12 @@ jobs:
- name: Run unit tests
run: ./.github/workflows/scripts/run_unit_tests.sh
artifacts-windows:
name: artifacts (windows)
artifact-windows:
name: artifact (windows)
runs-on: windows-2022
needs: cache-deps-windows
timeout-minutes: 45
env:
GOPATH: 'D:\golang\go'
GOCACHE: 'D:\golang\cache'
GOMODCACHE: 'D:\golang\modcache'
permissions:
contents: read
@ -563,24 +521,23 @@ jobs:
shell: msys2 {0}
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: Setup go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: 'go.mod'
cache: true
go-version: ${{ env.GO_VERSION }}
- name: Load cached deps
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
- name: Load cached build tools
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: .build
key: ${{ runner.os }}-tools-${{ hashFiles('.go-version','Makefile') }}
- name: Install msys2
uses: msys2/setup-msys2@d44ca8e88d8b43d56cf5670f91747359d5537f97 # v2.26.0
uses: msys2/setup-msys2@07aeda7763550b267746a772dcea5e5ac3340b36 # v2
with:
msystem: MINGW64
update: true
@ -588,22 +545,22 @@ jobs:
git base-devel mingw-w64-x86_64-toolchain zip unzip
- name: Build binaries
run: make build
- name: Setup executables cache
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: ./bin/
key: ${{ runner.os }}-executables-${{ github.sha }}
- name: Build artifacts
run: ./.github/workflows/scripts/build_artifacts.sh ${{ runner.os }}
- name: Archive artifacts
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4
- name: Archive binaries
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3
with:
name: binaries-windows
name: bin-windows
path: ./bin/
- name: Archive artifacts
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3
with:
name: binaries
path: ./artifacts/
success:
runs-on: ubuntu-22.04
needs: [lint, unit-test, unit-test-race-detector, artifacts, integration, integration-k8s, lint-windows, unit-test-windows, artifacts-windows, integration-windows]
runs-on: ubuntu-20.04
needs: [lint, unit-test, unit-test-race-detector, artifacts, integration, lint-windows, unit-test-windows, artifact-windows, integration-windows]
timeout-minutes: 30
permissions:
contents: read

View File

@ -3,23 +3,25 @@ on:
push:
tags:
- 'v[0-9].[0-9]+.[0-9]+'
env:
GO_VERSION: 1.21.1
jobs:
cache-deps:
name: cache-deps (linux)
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: Setup go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: 'go.mod'
go-version: ${{ env.GO_VERSION }}
- name: Setup dep cache
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
@ -28,7 +30,7 @@ jobs:
lint:
name: lint (linux)
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
needs: cache-deps
permissions:
@ -36,18 +38,18 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: Setup go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: 'go.mod'
go-version: ${{ env.GO_VERSION }}
- name: Load cached deps
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
- name: Setup build tool cache
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: .build
key: ${{ runner.os }}-tools-${{ hashFiles('.go-version','Makefile') }}
@ -63,7 +65,7 @@ jobs:
unit-test:
strategy:
matrix:
OS: [ubuntu-22.04, macos-latest]
OS: [ubuntu-20.04, macos-latest]
runs-on: ${{ matrix.OS }}
needs: cache-deps
@ -72,13 +74,13 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: Setup go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: 'go.mod'
go-version: ${{ env.GO_VERSION }}
- name: Load cached deps
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
@ -87,7 +89,7 @@ jobs:
unit-test-race-detector:
name: unit-test (linux with race detection)
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
needs: cache-deps
permissions:
@ -95,13 +97,13 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: Setup go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: 'go.mod'
go-version: ${{ env.GO_VERSION }}
- name: Load cached deps
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
@ -110,7 +112,7 @@ jobs:
artifacts:
name: artifacts (linux)
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
needs: [cache-deps, images]
timeout-minutes: 30
@ -119,15 +121,15 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: Setup go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: 'go.mod'
go-version: ${{ env.GO_VERSION }}
- name: Install regctl
uses: regclient/actions/regctl-installer@ce5fd131e371ffcdd7508b478cb223b3511a9183 # main
uses: regclient/actions/regctl-installer@b6614f5f56245066b533343a85f4109bdc38c8cc # main
- name: Download archived images
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
with:
name: images
path: .
@ -137,14 +139,14 @@ jobs:
- name: Build artifacts
run: ./.github/workflows/scripts/build_artifacts.sh ${{ runner.os }}
- name: Archive artifacts
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3
with:
name: binaries-linux
name: binaries
path: ./artifacts/
images:
name: images (linux)
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
needs: [cache-deps]
permissions:
@ -152,27 +154,27 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: Setup go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: 'go.mod'
go-version: ${{ env.GO_VERSION }}
- name: Load cached deps
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
- name: Load cached build tools
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: .build
key: ${{ runner.os }}-tools-${{ hashFiles('.go-version','Makefile') }}
- name: Build images
run: TAG=${GITHUB_REF##refs/tags/v} make images-no-load
run: make images-no-load
- name: Export images
run: tar -czvf images.tar.gz *-image.tar
- name: Archive images
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3
with:
name: images
path: images.tar.gz
@ -180,19 +182,19 @@ jobs:
images-windows:
name: images (windows)
runs-on: windows-2022
needs: artifacts-windows
needs: artifact-windows
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Load cached executables
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: Download artifacts
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
with:
name: bin-windows
path: ./bin/
key: ${{ runner.os }}-executables-${{ github.sha }}
- name: Build images
run: make images-windows
- name: Export images
@ -200,20 +202,20 @@ jobs:
docker save spire-server-windows:latest-local spire-agent-windows:latest-local oidc-discovery-provider-windows:latest-local -o images-windows.tar
gzip images-windows.tar
- name: Archive images
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3
with:
name: images-windows
path: images-windows.tar.gz
build-matrix:
name: Build matrix
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
needs: [cache-deps]
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- id: set-matrix
name: Collect versions
run: |
@ -225,10 +227,9 @@ jobs:
test: ${{ steps.set-matrix.outputs.test }}
integration:
name: integration (${{ matrix.arch }}) (${{ strategy.job-index}}/${{ strategy.job-total }})
runs-on: ${{ matrix.runs-on }}
name: integration (linux)
runs-on: ubuntu-20.04
needs: [cache-deps, images]
timeout-minutes: 45
permissions:
contents: read
@ -236,17 +237,11 @@ jobs:
strategy:
fail-fast: false
matrix:
arch: [x64, arm64]
num_runners: [10]
runner_id: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
include:
- arch: x64
runs-on: ubuntu-22.04
- arch: arm64
runs-on: ubuntu-22.04-arm
num_runners: [5]
runner_id: [1, 2, 3, 4, 5]
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
with:
# The "upgrade" integration test needs the history to ensure
# that the version number in the source code has been bumped as
@ -263,23 +258,23 @@ jobs:
- name: Fix tag annotations
run: git fetch --tags --force
- name: Setup go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: 'go.mod'
go-version: ${{ env.GO_VERSION }}
- name: Install regctl
uses: regclient/actions/regctl-installer@ce5fd131e371ffcdd7508b478cb223b3511a9183 # main
uses: regclient/actions/regctl-installer@b6614f5f56245066b533343a85f4109bdc38c8cc # main
- name: Load cached deps
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
- name: Load cached build tools
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: .build
key: ${{ runner.os }}-tools-${{ hashFiles('.go-version','Makefile') }}
- name: Download archived images
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
with:
name: images
path: .
@ -292,15 +287,14 @@ jobs:
NUM_RUNNERS: ${{ matrix.num_runners }}
THIS_RUNNER: ${{ matrix.runner_id }}
TERM: dumb
IGNORE_SUITES: ${{ matrix.arch == 'arm64' && 'suites/upstream-authority-ejbca' || '' }} # Waiting for EJBCA to support arm64 (https://github.com/spiffe/spire/issues/6060)
# We don't need to specify CICD_TARGET_BRANCH since the upgrade
# integration test will detect the annotated tag for version checking.
# CICD_TARGET_BRANCH:
run: ./.github/workflows/scripts/split.sh | xargs ./test/integration/test.sh
integration-k8s:
name: integration-k8s-${{ matrix.test[0] }}-${{ matrix.arch }}
runs-on: ${{ matrix.runs-on }}
name: integration-k8s
runs-on: ubuntu-20.04
needs: [cache-deps, images, build-matrix]
timeout-minutes: 45
@ -310,21 +304,13 @@ jobs:
strategy:
fail-fast: false
matrix:
arch: [x64, arm64]
include:
- arch: x64
runs-on: ubuntu-22.04
num_runners: 1
runner_id: 1
- arch: arm64
runs-on: ubuntu-22.04-arm
num_runners: 1
runner_id: 1
#Test elements should be added as [KubeCTLVersion, K8s-image, KindVersion]
test: ${{ fromJson(needs.build-matrix.outputs.test) }}
num_runners: [1]
runner_id: [1]
#Test elements should be added as [KubeCTLVersion, K8s-image, KindVersion]
test: ${{ fromJson(needs.build-matrix.outputs.test) }}
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
with:
# The "upgrade" integration test needs the history to ensure
# that the version number in the source code has been bumped as
@ -332,23 +318,23 @@ jobs:
# fetch depth of zero.
fetch-depth: 0
- name: Setup go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: 'go.mod'
go-version: ${{ env.GO_VERSION }}
- name: Install regctl
uses: regclient/actions/regctl-installer@ce5fd131e371ffcdd7508b478cb223b3511a9183 # main
uses: regclient/actions/regctl-installer@b6614f5f56245066b533343a85f4109bdc38c8cc # main
- name: Load cached deps
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
- name: Load cached build tools
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: .build
key: ${{ runner.os }}-tools-${{ hashFiles('.go-version','Makefile') }}
- name: Download archived images
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
with:
name: images
path: .
@ -380,23 +366,23 @@ jobs:
shell: msys2 {0}
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: Setup go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: 'go.mod'
go-version: ${{ env.GO_VERSION }}
- name: Load cached deps
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
- name: Load cached build tools
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: .build
key: ${{ runner.os }}-tools-${{ hashFiles('.go-version','Makefile') }}
- name: Install msys2
uses: msys2/setup-msys2@d44ca8e88d8b43d56cf5670f91747359d5537f97 # v2.26.0
uses: msys2/setup-msys2@07aeda7763550b267746a772dcea5e5ac3340b36 # v2
with:
msystem: MINGW64
update: true
@ -404,7 +390,7 @@ jobs:
install: >-
git base-devel mingw-w64-x86_64-toolchain unzip
- name: Download archived images
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
with:
name: images-windows
path: .
@ -423,13 +409,13 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: Setup go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: 'go.mod'
go-version: ${{ env.GO_VERSION }}
- name: Setup dep cache
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
@ -449,23 +435,23 @@ jobs:
shell: msys2 {0}
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: Setup go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: 'go.mod'
go-version: ${{ env.GO_VERSION }}
- name: Load cached deps
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
- name: Setup build tool cache
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: .build
key: ${{ runner.os }}-tools-${{ hashFiles('.go-version','Makefile') }}
- name: Install msys2
uses: msys2/setup-msys2@d44ca8e88d8b43d56cf5670f91747359d5537f97 # v2.26.0
uses: msys2/setup-msys2@07aeda7763550b267746a772dcea5e5ac3340b36 # v2
with:
msystem: MINGW64
update: true
@ -491,18 +477,18 @@ jobs:
shell: msys2 {0}
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: Setup go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: 'go.mod'
go-version: ${{ env.GO_VERSION }}
- name: Load cached deps
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
- name: Install msys2
uses: msys2/setup-msys2@d44ca8e88d8b43d56cf5670f91747359d5537f97 # v2.26.0
uses: msys2/setup-msys2@07aeda7763550b267746a772dcea5e5ac3340b36 # v2
with:
msystem: MINGW64
update: true
@ -511,8 +497,8 @@ jobs:
- name: Run unit tests
run: ./.github/workflows/scripts/run_unit_tests.sh
artifacts-windows:
name: artifacts (windows)
artifact-windows:
name: artifact (windows)
runs-on: windows-2022
needs: cache-deps-windows
@ -524,23 +510,23 @@ jobs:
shell: msys2 {0}
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: Setup go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version-file: 'go.mod'
go-version: ${{ env.GO_VERSION }}
- name: Load cached deps
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
- name: Load cached build tools
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
with:
path: .build
key: ${{ runner.os }}-tools-${{ hashFiles('.go-version','Makefile') }}
- name: Install msys2
uses: msys2/setup-msys2@d44ca8e88d8b43d56cf5670f91747359d5537f97 # v2.26.0
uses: msys2/setup-msys2@07aeda7763550b267746a772dcea5e5ac3340b36 # v2
with:
msystem: MINGW64
update: true
@ -550,41 +536,35 @@ jobs:
run: make build
- name: Build artifacts
run: ./.github/workflows/scripts/build_artifacts.sh ${{ runner.os }}
- name: Setup executables cache
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
- name: Archive binaries
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3
with:
name: bin-windows
path: ./bin/
key: ${{ runner.os }}-executables-${{ github.sha }}
- name: Archive artifacts
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3
with:
name: binaries-windows
name: binaries
path: ./artifacts/
publish-artifacts:
runs-on: ubuntu-22.04
needs: [lint, unit-test, unit-test-race-detector, artifacts, integration, integration-k8s, lint-windows, unit-test-windows, artifacts-windows, integration-windows]
runs-on: ubuntu-20.04
needs: [lint, unit-test, unit-test-race-detector, artifacts, integration, lint-windows, unit-test-windows, artifact-windows, integration-windows]
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Download archived Linux artifacts
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: Download archived artifacts
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
with:
name: binaries-linux
name: binaries
path: ./artifacts/
- name: Download archived Windows artifacts
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: binaries-windows
path: ./artifacts/
- name: Create Release
env:
# GH_REPO is required for older releases of `gh`. Until we're
# reasonably confident that the gh release is new enough,
# reasonably confident that that the gh release is new enough,
# set GH_REPO to the repository to create the release in.
#
# See https://github.com/cli/cli/issues/3556
@ -594,29 +574,32 @@ jobs:
run: gh release create "${GITHUB_REF#refs/tags/}" ./artifacts/*.zip ./artifacts/*.tar.gz ./artifacts/*.txt --title "${GITHUB_REF#refs/tags/}"
publish-images:
runs-on: ubuntu-22.04
needs: [lint, unit-test, unit-test-race-detector, artifacts, integration, integration-k8s, lint-windows, unit-test-windows, artifacts-windows, integration-windows]
runs-on: ubuntu-20.04
needs: [lint, unit-test, unit-test-race-detector, artifacts, integration, lint-windows, unit-test-windows, artifact-windows, integration-windows]
permissions:
contents: read
id-token: write
packages: write
env:
COSIGN_EXPERIMENTAL: 1
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- name: Install cosign
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
uses: sigstore/cosign-installer@11086d25041f77fe8fe7b9ea4e48e3b9192b8f19 # v3.1.2
with:
cosign-release: v2.2.3
cosign-release: v1.13.1
- name: Install regctl
uses: regclient/actions/regctl-installer@ce5fd131e371ffcdd7508b478cb223b3511a9183 # main
uses: regclient/actions/regctl-installer@b6614f5f56245066b533343a85f4109bdc38c8cc # main
- name: Download archived images
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
with:
name: images
path: .
- name: Log in to GHCR
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
with:
registry: ghcr.io
username: ${{ github.actor }}

View File

@ -23,13 +23,6 @@
declare -A tags_map
for element in "${tags_sorted[@]}"; do
# Skip 1.32.1 until either a new version of kind is released the problem
# with the kindest/node:1.32.1 image is fixed. See upstream kind issue:
# https://github.com/kubernetes-sigs/kind/issues/3853
if [[ "$element" == "v1.32.1" ]]; then
continue
fi
# Element is in this form: "X.XX.YY"
# If not, continue
num_dots=$(echo "$element" | grep -o '\.' | wc -l)

View File

@ -68,5 +68,5 @@ for img in "${OCI_IMAGES[@]}"; do
image_digest="$(jq -r '.manifests[0].digest' "${ROOTDIR}oci/${img}/index.json")"
cosign sign -y "${registry}/${img}@${image_digest}"
cosign sign "${registry}/${img}@${image_digest}"
done

View File

@ -18,7 +18,7 @@ for FILE in test/integration/suites/*; do
job_set[$current_runner]+="${FILE##test/integration/} "
((current_runner++))
if [ "$current_runner" -gt "$NUM_RUNNERS" ]; then
if [ $current_runner -gt "$NUM_RUNNERS" ]; then
current_runner=1
fi
done

View File

@ -18,7 +18,7 @@ for FILE in test/integration/suites/k8s*; do
job_set[$current_runner]+="${FILE##test/integration/} "
((current_runner++))
if [ "$current_runner" -gt "$NUM_RUNNERS" ]; then
if [ $current_runner -gt "$NUM_RUNNERS" ]; then
current_runner=1
fi
done

View File

@ -11,29 +11,12 @@ jobs:
issues: write
pull-requests: write
steps:
- uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0
- uses: actions/stale@1160a2240286f5da8ec72b1c0816ce2481aabf84 # v8.0.0
with:
days-before-issue-stale: 365 # 1 year
days-before-issue-close: 30
stale-issue-label: "stale"
exempt-issue-labels: "blocked" # Ignore blocked issues
stale-issue-message: "This issue is stale because it has been open for 365 days with no activity."
close-issue-message: "This issue was closed because it has been inactive for 30 days since being marked as stale."
days-before-pr-stale: -1 # Don't handle PRs
days-before-pr-close: -1 # Don't handle PRs
process-stale-blocked-issues:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0
with:
only-labels: "blocked"
days-before-issue-stale: 30
days-before-issue-close: -1 # Don't close blocked issues
stale-issue-label: "stale"
stale-issue-message: "This issue has been in the blocked state for 30 days, marking as stale so the blocking issue is re-checked."
days-before-pr-stale: -1 # Don't handle PRs
days-before-pr-close: -1 # Don't handle PRs

5
.gitignore vendored
View File

@ -1,7 +1,6 @@
.build
.cache
.data
.envrc
.glide
.tmp
.DS_Store
@ -37,7 +36,3 @@ tools/spire-plugingen/spire-plugingen
# oci artifacts
*-image.tar
oci/
# Go workspace files
go.work
go.work.sum

View File

@ -1 +1 @@
1.24.4
1.21.1

View File

@ -1,87 +1,31 @@
version: "2"
run:
# timeout for analysis, e.g. 30s, 5m, default is 1m
timeout: 12m
skip-dirs:
- testdata$
- test/mock
skip-files:
- ".*\\.pb\\.go"
linters:
enable:
- bodyclose
- copyloopvar
- durationcheck
- errorlint
- exptostd
- gocritic
- goimports
- revive
- gosec
- intrange
- mirror
- misspell
- nakedret
- nilnesserr
- nolintlint
- predeclared
- reassign
- revive
- unconvert
- unparam
- wastedassign
- whitespace
settings:
govet:
enable:
- sortslice
- unusedwrite
revive:
confidence: 0
rules:
- name: atomic
- name: bool-literal-in-expr
- name: constant-logical-expr
- name: context-as-argument
- name: datarace
- name: error-naming
- name: error-return
- name: errorf
- name: identical-branches
- name: if-return
- name: increment-decrement
- name: modifies-value-receiver
- name: optimize-operands-order
- name: range
- name: receiver-naming
- name: redundant-import-alias
- name: redundant-test-main-exit
- name: string-of-int
- name: time-equal
- name: unconditional-recursion
- name: unnecessary-stmt
- name: unreachable-code
- name: use-any
- name: use-errors-new
- name: useless-break
- name: var-declaration
- name: waitgroup-by-value
staticcheck:
checks:
- all
- -ST1003
- -QF1001
- -QF1008
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
rules:
- linters:
- gosec
path: (.*_test\.go$)|(^test/.*)
text: integer overflow conversion
- linters:
- revive
text: Import alias "v1" is redundant
formatters:
enable:
- gofmt
- goimports
exclusions:
generated: lax
- gocritic
- nolintlint
linters-settings:
revive:
# minimal confidence for issues, default is 0.8
confidence: 0.0

View File

@ -1,3 +0,0 @@
golangci-lint v2.1.6
markdown_lint v0.37.0
protoc 29.4

View File

@ -20,7 +20,6 @@ Known end users with notable contributions to the advancement of the project inc
SPIFFE and SPIRE are being used by numerous other companies, both large and small, to build higher layer products and services. The list includes but is not limited to:
* AccuKnox
* Amazon
* Arm
* Cisco
@ -43,24 +42,23 @@ SPIFFE and SPIRE have integrations available with a number of open-source projec
* [App Mesh Controller](https://github.com/aws/aws-app-mesh-controller-for-k8s)
* [Athenz](https://github.com/yahoo/athenz)
* [Cert-Manager](https://github.com/cert-manager/csi-driver-spiffe)
* [Consul](https://github.com/hashicorp/consul)
* [Dapr](https://github.com/dapr)
* [Docker](https://github.com/containerd/containerd)
* [Emissary](https://github.com/github/emissary)
* [Envoy](https://github.com/envoyproxy/envoy)
* [Ghostunnel](https://github.com/square/ghostunnel)
* [Cert-Manager](https://github.com/cert-manager/csi-driver-spiffe)
* [Consul](https://github.com/hashicorp/consul)
* [Dapr](https://github.com/dapr)
* [Docker](https://github.com/containerd/containerd)
* [Emissary](https://github.com/github/emissary)
* [Envoy](https://github.com/envoyproxy/envoy)
* [Ghostunnel](https://github.com/square/ghostunnel)
* [gRPC](https://pkg.go.dev/github.com/spiffe/go-spiffe/v2/examples/spiffe-grpc)
* [Hamlet](https://github.com/vmware/hamlet)
* [Istio](https://github.com/istio/istio)
* [Knox](https://github.com/pinterest/knox)
* [Kubernetes](https://github.com/kubernetes/kubernetes)
* [Linkerd](https://github.com/linkerd/linkerd2)
* [NGINX](http://hg.nginx.org/nginx/)
* [Parsec](https://github.com/parallaxsecond/parsec)
* [Sigstore](https://github.com/sigstore/fulcio)
* [Tekton](https://github.com/tektoncd/chains)
* [Tornjak](https://github.com/spiffe/tornjak)
* [Hamlet](https://github.com/vmware/hamlet)
* [Istio](https://github.com/istio/istio)
* [Knox](https://github.com/pinterest/knox)
* [Kubernetes](https://github.com/kubernetes/kubernetes)
* [NGINX](http://hg.nginx.org/nginx/)
* [Parsec](https://github.com/parallaxsecond/parsec)
* [Sigstore](https://github.com/sigstore/fulcio)
* [Tekton](https://github.com/tektoncd/chains)
* [Tornjak](https://github.com/spiffe/tornjak)
* [Traefik](https://github.com/traefik/traefik)
## Case Studies/User Stories

View File

@ -1,452 +1,5 @@
# Changelog
## [1.12.4] - 2025-07-01
### Added
- `k8s_configmap` BundlePublisher plugin (#6105, #6139)
- UpstreamAuthority.SubscribeToLocalBundle RPC to stream updates in the local trust bundle (#6090)
- Integration tests running on ARM64 platform (#6059)
- The OIDC Discovery Provider can now read the trust bundle from a file (#6025)
### Changed
- The "Container id not found" log message in the `k8s` WorkloadAttestor has been lowered to Debug level (#6128)
- Improvements in lookup performance for entries (#6100, #6034)
- Agent no longer pulls the bundle from `trust_bundle_url` if it is not required (#6065)
### Fixed
- The `subject_types_supported` value in the discovery document is now properly populated by the OIDC Discovery Provider (#6126)
- SPIRE Server gRPC servers are now gracefully stopped (#6076)
## [1.12.3] - 2025-06-17
### Security
- Fixed an issue in spire-agent where the WorkloadAPI.ValidateJWTSVID endpoint did not enforce the presence of the exp (expiration) claim in JWT-SVIDs, as required by the SPIFFE specification.
This vulnerability has limited impact: by default, SPIRE does not issue JWT-SVIDs without an expiration claim. Exploitation would require federating with a misconfigured or non-compliant trust domain.
Thanks to Edoardo Geraci for reporting this issue.
## [1.12.2] - 2025-05-19
### Fixed
- Regression where PolicyCredentials set by CredentialComposer plugins were not correctly applied to CA certificates. (#6074)
## [1.12.1] - 2025-05-06
### Added
- Support for Unix sockets in trust bundle URLs (#5932)
- Documentation improvements and additions (#5989, #6012)
### Changed
- `sql_transaction_timeout` replaced by `event_timeout` and value reduced to 15 minutes (#5966)
- Experimental events-based cache performance improvements by batch fetching updated entries (#5970)
- Improved error messages when retrieving CGroups (#6030).
### Fixed
- Corrected invalid `user-agent` value in OIDC Discovery Provider debug logs (#5981).
## [1.12.0] - 2025-03-21
### Added
- Support for any S3 compatible object storage such as MinIO in the `aws_s3` BundlePublisher plugin (#5757)
- Support for Rego V1 in the authorization policy engine (#5769)
- Support for SAN-based selectors in the `x509pop` NodeAttestor plugin (#5775)
### Changed
- Agents now use the SyncAuthorizedEntries API for periodically synchronization of authorized entries by default (#5906)
- Timestamps in logs are now formatted to include nanoseconds (#5798)
- Improved entry lookup performance in NewJWTSVID and BatchNewX509SVID server RPCs (#5819)
- Increased the maximum number of idle database connections to 100 (#5853)
- The maximum idle time per database connection is now set to 30 seconds (#5853)
- Small documentation improvements (#5873, #5876)
- The experimental events-based cache now supports reading events from read-only replicas when data staleness is tolerated, enhancing read performance (#5911)
- The `use_legacy_downstream_x509_ca_ttl` server setting is now set to false by default (#5917)
### Deprecated
- `use_sync_authorized_entries` experimental agent setting (#5906)
- `use_legacy_downstream_x509_ca_ttl` server setting (#5917)
### Removed
- The deprecated `k8s_sat` NodeAttestor plugin (#5703)
### Fixed
- Issue where agents did not receive entry updates when new entries with the same entry ID were created while `use_sync_authorized_entries` was enabled (#5764)
## [1.11.3] - 2025-06-17
### Security
- Fixed an issue in spire-agent where the WorkloadAPI.ValidateJWTSVID endpoint did not enforce the presence of the exp (expiration) claim in JWT-SVIDs, as required by the SPIFFE specification.
This vulnerability has limited impact: by default, SPIRE does not issue JWT-SVIDs without an expiration claim. Exploitation would require federating with a misconfigured or non-compliant trust domain.
Thanks to Edoardo Geraci for reporting this issue.
## [1.11.2] - 2025-02-13
### Added
- `gcp_secretmanager` SVIDStore plugin now supports specifying the regions where secrets are created (#5718)
- Support for expanding environment variables in the OIDC Discovery Provider configuration (#5689)
- Support for optionally enabling `trust_domain` label for all metrics (#5673)
- The JWKS URI returned in the discovery document can now be configured in the OIDC Discovery Provider (#5690)
- A server path prefix can now be specified in the OIDC Discovery Provider (#5690)
### Changed
- Small documentation improvements (#5809, #5720)
### Fixed
- Regression in the hydration of the experimental event-based cache that caused a delay in availability (#5842)
- Do not log an error when the Envoy SDS v3 API connection has been closed cleanly (#5835)
- SVIDStore plugins to properly parse metadata in entry selectors containing ':' characters (#5750)
- Compatibility with deployments that use a server port other than 443 when the `jwt_issuer` configuration is set in the OIDC Discovery Provider (#5690)
- Domain verification is now properly done when setting the `jwt_issuer` configuration in the OIDC Discovery Provider (#5690)
### Security
- Fixed to properly call the CompareObjectHandles function when it's available on Windows systems, as an extra security measure in the peertracker (#5749)
## [1.11.1] - 2024-12-12
### Added
- The Go based text/template engine used in various plugins has been extended to include a set of functions from the SPRIG library (#5593, #5625)
- The JWT-SVID cache in the agent is now configurable (#5633)
- The JWT issuer is now configurable in the OIDC Discovery Provider (#5657)
### Changed
- CA journal now relies on the authority ID instead of the issued time when updating the status of keys (#5622)
### Fixed
- Spelling and grammar fixes (#5571)
- Handling of IPv6 address consistently for the binding address of the server and health checks (#5623)
- Link to Telemetry documentation in the Contributing guide (#5650)
- Handling of registration entries with revision number 0 when the agent syncs entries with the server (#5680)
### Known Issues
- Setting the new `jwt_issuer` configuration property in oidc-discovery-provider is not compatible with deployments that use a server port other than 443 (#5696)
- Domain verification is bypassed when setting the new `jwt_issuer` configuration property in oidc-discovery-provider (#5697)
## [1.11.0] - 2024-10-24
### Added
- Support for forced rotation and revocation (<https://github.com/orgs/spiffe/projects/21>)
- New EJBCA UpstreamAuthority plugin for SPIRE Server (#5378)
- Support for variables in templates contained in the config file (#5576)
- Support for the configuration validation RPC on all built-in plugins (#5303)
- Improved logging when built-in plugins panic (#5476)
- Improved CPU and memory resource usage for concurrent Kubernetes Workload attestation (#5408)
- Documentation additions and improvements (#5589, #5588, #5499, #5433, #5430, #5269)
### Changed
- SPIRE Agent LRU identity cache is now unconditionally enabled. The LRU size can be controlled via the `x509_svid_cache_max_size` configuration option. (#5383, #5531)
- Entry API RPCs return per-entry InvalidArgument status when creating/updating malformed entries (#5506)
- Support for CGroups v2 in K8s and Docker workload attestors is now enabled by default (#5454)
### Removed
- Deprecated -ttl flag from the SPIRE Server `entry create` and `entry update` commands (#5483)
- Official support for MySQL 5.X. While SPIRE may continue to work with this version, no explicit testing will be performed by the project (#5487)
### Fixed
- Missing TrustDomain field passed to x509pop path template (#5577)
- Behavior in the experimental events-based cache causing duplicate entries/agents evaluation in the same cycle (#5509)
## [1.10.4] - 2024-09-12
### Fixed
- Add missing commits to spire-plugin-sdk and spire-api-sdk releases (spiffe/spire-api-sdk#66, spiffe/spire-plugin-sdk#39)
## [1.10.3] - 2024-09-03
### Fixed
- Regression in agent health check, requiring the agent to have an SVID on disk to be healthy (#5459)
## [1.10.2] - 2024-09-03
### Added
- `http_challenge` NodeAttestor plugin (#4909)
- Experimental support for validating container image signatures through Sigstore selectors in the docker Workload Attestor (#5272)
- Metrics for monitoring the event-based cache (#5411)
### Changed
- Delegated Identity API to allow subscription by process ID (#5272)
- Agent Debug endpoint to count SVIDs by type (#5352)
- Agent health check to report an unhealthy status until the Agent SVID is attested (#5298)
- Small documentation improvements (#5393)
### Fixed
- `aws_iid` NodeAttestor to properly handle multiple network interfaces (#5300)
- Server configuration to correctly propagate the `sql_transaction_timeout` setting in the experimental events-based cache (#5345)
## [1.10.1] - 2024-08-01
### Added
- New Grafana dashboard template (#5188)
- `aws_rolesanywhere_trustanchor` BundlePublisher plugin (#5048)
### Changed
- `spire` UpstreamAuthority to optionally use the Preferred TTL on intermediate authorities (#5264)
- Federation endpoint to support custom bundle and certificates for authorization (#5163)
- Small documentation improvements (#5235, #5220)
### Fixed
- Event-based cache to handle events missed at the cache startup (#5289)
- LRU cache to no longer send update notifications to all subscribers (#5281)
## [1.10.0] - 2024-06-24
### Added
- Plugin reconfiguration support using the `plugin_data_file` configurable (#5166)
### Changed
- SPIRE Server and OIDC provider images to use non-root users (#4967, #5227)
- `k8s_psat` NodeAttestor attestor to no longer fail when a cluster is not configured (#5216)
- Agents are required to renew SVIDs through re-attestation when using a supporting Node Attestor (#5204)
- Small documentation improvements (#5181, #5189)
- Evicted agents that support reattestation can now reattest without being restarted (#4991)
### Fixed
- PSAT node attestor to cross-check the audience fields (#5142)
- Events-based cache to handle out of order events (#5071)
### Deprecated
- `x509_svid_cache_max_size` and `disable_lru_cache` in agent configuration (#5150)
### Removed
- The deprecated `disable_reattest_to_renew` agent configurable (#5217)
- The deprecated `key_metadata_file` configurable from the `aws_kms`, `azure_key_vault` and `gcp_kms` server KeyManagers (#5207)
- The deprecated `use_msi` configurable from the `azure_key_vault` server KeyManager and `azure_msi` NodeAttestor (#5207, #5209)
- The deprecated `exclude_sn_from_ca_subject` server configurable (#5203)
- Agent no longer cleans up deprecated bundle and SVID files (#5205)
- The CA journal file is no longer stored on disk, and existing CA journal files are cleaned up (#5202)
## [1.9.6] - 2024-05-14
### Added
- Opt-in support for CGroups v2 in K8s and Docker workload attestors (#5076)
- `gcp_cloudstorage` BundlePublisher plugin (#4961)
- The `aws_iid` node attestor can now check if the AWS account ID is part of an AWS Organization (#4838)
- More filtering options to count and show entries and agents (#4714)
### Changed
- Credential composer to not convert timestamp related claims (i.e., exp and iat) to floating point values (#5115)
- FetchJWTBundles now returns an empty collection of keys instead of null (#5031)
### Fixed
- Using expired tokens when connecting to database (#5119)
- Server no longer tries to create JWT authority when X.509 authority fails (#5064)
- Issues in experimental events-based entry cache (#5030, #5037, #5042)
## [1.9.5] - 2024-05-07
### Security
- Updated to Go 1.21.10 to address CVE-2024-24788
## [1.9.4] - 2023-04-05
### Security
- Updated to google.golang.org/grpc v1.62.2 and golang.org/x/net v0.24.0 to address CVE-2023-45288
## [1.9.3] - 2024-04-03
### Security
- Updated to Go 1.21.9 to address CVE-2023-45288
- Limit the preallocation of memory when making paginated requests to the ListEntries and ListAgents RPCs
## [1.9.2] - 2024-03-25
### Added
- Support for AWS IAM-based authentication with AWS RDS backed databases (#4828)
- Support for adjusting the SPIRE Server log level at runtime (#4880)
- New `retry_bootstrap` option to SPIRE Agent to retry failed bootstrapping with SPIRE Server, with a backoff, in lieu of failing the startup process (#4597)
- Improved logging (#4902, #4906)
- Documentation improvements (#4895, #4951, #4907)
## [1.9.1] - 2024-03-05
### Security
- Update Go to v1.21.8 to patch CVE-2024-24783
## [1.9.0] - 2024-02-22
### Added
- `uniqueid` CredentialComposer plugin that adds the x509UniqueIdentifier attribute to workload X509-SVIDs (#4862)
- Agent's Admin API has now a default location defined (#4856)
- Partial selectors from workload attestation are now logged when attestation is interrupted (#4846)
- X509-SVIDs minted by SPIRE can now include wildcards in the DNS names (#4814)
### Changed
- CA journal data is now stored in the datastore, removing the on-disk dependency of the server (#4690)
- `aws_kms`, `azure_key_vault`, and `gcp_kms` KeyManager plugins no longer require storing metadata files on disk (#4700)
- Bundle endpoint refresh hint now defaults to 5 minutes (#4847, #4888)
- Graceful shutdown is now blocked while built-in plugin RPCs drain (#4820)
- Entry cache hydration is now done with paginated requests to the datastore (#4721, #4826)
- Agents renew SVIDs through re-attestation by default when using a supporting Node Attestor (#4791)
- The SPIRE Agent LRU SVID cache is no longer experimental and is enabled by default (#4773)
- Small documentation improvements (#4764, #4787)
- Read-replicas are no longer used when hydrating the experimental events-based entry cache (#4868)
- Workload gRPC connections are now terminated when the peertracker liveness check fails instead of just failing the RPC calls (#4611)
### Fixed
- Missing creation of events in the experimental events-based cache entry when an entry was pruned (#4860)
- Bug in SPIRE Agent LRU SVID cache that caused health checks to fail (#4852)
- Refreshing of selectors of attested agents when using the experimental events-based entry cache (#4803)
### Deprecated
- `k8s_sat` NodeAttestor plugin (#4841)
### Removed
- X509-SVIDs issued by the server no longer have the x509UniqueIdentifier attribute as part of the subject (#4862)
## [1.8.11] - 2024-05-07
### Security
- Updated to Go 1.21.10 to address CVE-2024-24788
## [1.8.10] - 2023-04-05
### Security
- Updated to google.golang.org/grpc v1.62.2 and golang.org/x/net v0.24.0 to address CVE-2023-45288
## [1.8.9] - 2024-04-03
### Security
- Updated to Go 1.21.9 to address CVE-2023-45288
- Limit the preallocation of memory when making paginated requests to the ListEntries and ListAgents RPCs
## [1.8.8] - 2024-03-05
### Security
- Update Go to v1.21.8 to patch CVE-2024-24783
## [1.8.7] - 2023-12-21
### Added
- Agents can now be configured with an availability target, which establishes the minimum amount of time desired to gracefully handle server or agent downtime, influencing how aggressively X509-SVIDs should be rotated (#4599)
- SyncAuthorizedEntries RPC, which allows agents to only sync down changes instead of the entire set of entries. Agents can be configured to use this new RPC through the `use_sync_authorized_entries` experimental setting (#4648)
- Experimental support for an events based entry cache which reduces overhead on the database (#4379, #4411, #4527, #4451, #4562, #4723, #4731)
### Changed
- The maximum number of open database connections in the datastore now defaults to 100 instead of unlimited (#4656)
- Agents now shut down when they can't synchronize entries with the server due to an unknown authority error (#4617)
### Removed
- Agents no longer maintains agent SVID and bundle information in the legacy paths in the data directory (#4717)
## [1.8.6] - 2023-12-07
### Security
- Updated to Go 1.21.5 to address CVE-2023-39326
## [1.8.5] - 2023-11-22
### Added
- All credential types supported by Azure can now be used in `azure_msi` NodeAttestor plugin and `azure_key_vault` KeyManager plugin (#4568)
- `EnableHostnameLabel` field in Server and Agent `telemetry` configuration section that enables addition of a hostname label to metrics (#4584)
### Changed
- Agent SDS API now provides a SPIFFEValidationContext as the default CertificateValidationContext when the Envoy version cannot be determined (#4618)
- Server CAs now contain a `serialNumber` attribute in the `Subject` DN (#4585)
- Improved accuracy of Agent log message for SVID renewal events (#4654)
### Deprecated
- `use_msi` configuration fields in `azure_msi` NodeAttestor plugin and `azure_key_vault` KeyManager plugin are deprecated in favor of the chained Azure SDK credential loading strategy (#4568)
### Fixed
- Agent SDS API now provides correct CertificateValidationContext when Envoy registered in SPIRE after the first SDS request (#4611)
## [1.8.4] - 2023-11-07
### Security
- Updated to Go 1.21.4 to address CVE-2023-45283, CVE-2023-45284
## [1.8.3] - 2023-10-25
### Added
- SPIRE Agent distributes sync requests to the SPIRE server to mitigate thundering herd situations (#4534)
- Allow configuring prefixes for all metrics (#4535)
- Documentation improvements (#4579, #4569)
### Changed
- SPIRE Agent performs the initial sync more aggressively when tuned with a longer sync interval (#4479)
### Fixed
- Release artifacts have the correct version information (#4564)
- The SPIRE Agent `insecureBootstrap` and `trustBundleUrl` configurables are now mutually exclusive (#4532)
- Bug preventing JWT-SVIDs from being minted when a Credential Composer plugin is configured (#4489)
## [1.8.2] - 2023-10-12
### Security
- Updated to google.golang.org/grpc v1.58.3 and golang.org/x/net v0.17.0 to address CVE-2023-39325, CVE-2023-44487
## [1.8.1] - 2023-10-10
### Security
- Updated to Go 1.21.3 to address CVE-2023-39325, CVE-2023-44487
## [1.8.0] - 2023-09-20
### Added
@ -476,30 +29,6 @@ Thanks to Edoardo Geraci for reporting this issue.
- Server no longer cleans up stale data in the database on startup (#4443)
- Server no longer deletes entries with invalid SPIFFE IDs on startup (#4449)
## [1.7.6] - 2023-12-07
### Security
- Updated to Go 1.20.12 to address CVE-2023-39326
## [1.7.5] - 2023-11-07
### Security
- Updated to Go 1.20.11 to address CVE-2023-45283, CVE-2023-45284
## [1.7.4] - 2023-10-12
### Security
- Updated to google.golang.org/grpc v1.58.3 and golang.org/x/net v0.17.0 to address CVE-2023-39325, CVE-2023-44487
## [1.7.3] - 2023-10-10
### Security
- Updated to Go 1.20.10 to address CVE-2023-39325, CVE-2023-44487
## [1.7.2] - 2023-08-16
### Added
@ -1216,7 +745,7 @@ Thanks to Edoardo Geraci for reporting this issue.
- Regression preventing agent selectors from showing in `spire-server agent show` command (#2133)
- Issue in the token authentication method of the Vault Upstream Authority plugin (#2110)
- Reporting of errors in server entry cache telemetry (#2091)
- Agent logs an error and automatically shuts down when its SVID has expired, and it requires re-attestation (#2065)
- Agent logs an error and automatically shuts down when its SVID has expired and it requires re-attestation (#2065)
## [0.12.1] - 2021-03-04
@ -1302,7 +831,7 @@ Thanks to Edoardo Geraci for reporting this issue.
- Fixed Kubernetes Workload Registrar issues (#1814, #1818, #1823)
- Fixed BatchCreateEntry return value to match docs, returning the contents of an entry if it already exists (#1824)
- Fixed issue preventing brand-new deployments from downgrading successfully (#1829)
- Fixed issue preventing brand new deployments from downgrading successfully (#1829)
- Fixed a regression introduced in 0.11.0 that caused external node attestor plugins that rely on binary data to fail (#1863)
## [0.11.0] - 2020-08-28
@ -1406,7 +935,7 @@ Thanks to Edoardo Geraci for reporting this issue.
## [0.9.0] - 2019-11-14
- Users can now opt out of workload executable hashing when enabling the workload path as a selector (#1078)
- Users can now opt-out of workload executable hashing when enabling the workload path as a selector (#1078)
- Added M3 support to telemetry and other telemetry and logging improvements (#1059, #1085, #1086, #1094, #1102, #1122,#1138,#1160,#1186,#1208)
- SQL auto-migration can be disabled (#1089)
- SQL schema compatibility checks are aligned with upgrade compatibility guarantees (#1089)

View File

@ -1,27 +1,27 @@
* @evan2645 @amartinezfayo @sorindumitru @MarcosDY @rturner3
* @evan2645 @amartinezfayo @azdagron @MarcosDY @rturner3
##########################################
# Maintainers
##########################################
# Evan Gilman
# SPIRL, Inc.
# VMware, Inc
# @evan2645
# Agustin Martínez Fayó
# Hewlett-Packard Enterprise
# @amartinezfayo
# Sorin Dumitru
# Bloomberg L.P.
# @sorindumitru
# Andrew Harding
# VMware, Inc
# @azdagron
# Marcos Yacob
# Hewlett-Packard Enterprise
# @MarcosDY
# Ryan Turner
# Cielara AI
# Uber Technologies, Inc
# @rturner3
##########################################
@ -29,5 +29,5 @@
##########################################
# Umair Khan
# Stacklet, Inc.
# Hewlett-Packard Enterprise
# @umairmkhan

View File

@ -45,7 +45,7 @@ toolchain and other build related files are cached under the `.build` folder
### Development in Docker
You can either build SPIRE on your host or in an Ubuntu docker container. In
You can either build SPIRE on your host or in a Ubuntu docker container. In
both cases you will use the same Makefile commands.
To build SPIRE within a container, first build the development image:
@ -105,14 +105,14 @@ Packages should be exported through interfaces. Interaction with packages must b
interfaces
Interfaces should be defined in their own file, named (in lowercase) after the name of the
interface. e.g. `foodata.go` implements `type FooData any`
interface. eg. `foodata.go` implements `type FooData interface{}`
### Metrics
As much as possible, label names should be constants defined in the `telemetry` package. Additionally,
specific metrics should be centrally defined in the `telemetry` package or its subpackages. Functions
desiring metrics should delegate counter, gauge, timer, etc. creation to such packages.
The metrics emitted by SPIRE are listed in the [telemetry document](doc/telemetry/telemetry.md) and should be kept up to date.
The metrics emitted by SPIRE are listed in the [telemetry document](doc/telemetry.md) and should be kept up to date.
In addition, metrics should be unit-tested where reasonable.
@ -233,20 +233,14 @@ implementation can easily serve the needs for an entire suite of tests and
the behavior is in a centralized location when it needs to be updated. Fakes
are also less inclined to be impacted by changes to usage patterns.
## Example [direnv][direnv_link] .envrc
## Git hooks
We have committed a basic `.envrc.example`. If you use [direnv][direnv_link],
copy it into `.envrc`, edit as desired, and enable it with `direnv allow`. The
`.envrc` is `.gitignored`. Be aware that [source_env][source_env] is insecure
so keep your customizations in `.envrc`.
We have checked in a pre-commit hook which enforces `go fmt` styling. Please install it
before sending a pull request. From the project root:
[direnv_link]: https://direnv.net/
[source_env]: https://direnv.net/man/direnv-stdlib.1.html#codesourceenv-ltfileordirpathgtcode
## Project Tool Versions
This project uses a `.spire-tool-versions` file to centralize the versions of various tools used for
development, linting, and other tasks.
```shell
$ ln -s .githooks/pre-commit .git/hooks/pre-commit
```
## Reporting security vulnerabilities

View File

@ -1,8 +1,8 @@
# syntax = docker/dockerfile:1.6.0@sha256:ac85f380a63b13dfcefa89046420e1781752bab202122f8f50032edf31be0021
# syntax = docker/dockerfile:1.4.2@sha256:443aab4ca21183e069e7d8b2dc68006594f40bddf1b15bbd83f5137bd93e80e2
# Build stage
ARG goversion
FROM --platform=${BUILDPLATFORM} golang:${goversion}-alpine3.22 as base
FROM --platform=${BUILDPLATFORM} golang:${goversion}-alpine as base
WORKDIR /spire
RUN apk --no-cache --update add file bash clang lld pkgconfig git make
COPY go.* ./
@ -13,73 +13,76 @@ COPY . .
# xx is a helper for cross-compilation
# when bumping to a new version analyze the new version for security issues
# then use crane to lookup the digest of that version so we are immutable
# crane digest tonistiigi/xx:1.3.0
FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.5.0@sha256:0c6a569797744e45955f39d4f7538ac344bfb7ebf0a54006a0a4297b153ccf0f AS xx
# crane digest tonistiigi/xx:1.1.2
FROM --platform=$BUILDPLATFORM tonistiigi/xx@sha256:9dde7edeb9e4a957ce78be9f8c0fbabe0129bf5126933cd3574888f443731cda AS xx
FROM --platform=${BUILDPLATFORM} base as builder
ARG TAG
ARG TARGETPLATFORM
ARG TARGETARCH
COPY --link --from=xx / /
# For users that wish to run SPIRE containers as a non-root user,
# provide a default unprivileged user such that the default paths
# that SPIRE will try to read from, write to, and create at runtime
# can be given the correct file ownership/permissions at build time.
ARG spireuid=1000
ARG spiregid=1000
# Set up directories that SPIRE expects by default
# Set up base directories
RUN install -d -o root -g root -m 777 /spireroot
RUN install -d -o root -g root -m 755 /spireroot/etc/ssl/certs
RUN install -d -o root -g root -m 755 /spireroot/run
RUN install -d -o root -g root -m 755 /spireroot/var/lib
RUN install -d -o root -g root -m 1777 /spireroot/tmp
# Set up directories used by SPIRE
RUN install -d -o ${spireuid} -g ${spiregid} -m 755 /spireroot/etc/spire
RUN install -d -o ${spireuid} -g ${spiregid} -m 755 /spireroot/run/spire
RUN install -d -o ${spireuid} -g ${spiregid} -m 755 /spireroot/var/lib/spire
# Set up spire-server directories
RUN cp -r /spireroot /spireserverroot
RUN install -d -o ${spireuid} -g ${spiregid} -m 755 /spireserverroot/etc/spire/server
RUN install -d -o ${spireuid} -g ${spiregid} -m 755 /spireserverroot/run/spire/server/private
RUN install -d -o ${spireuid} -g ${spiregid} -m 755 /spireserverroot/var/lib/spire/server
# Set up spire-agent directories
RUN cp -r /spireroot /spireagentroot
RUN install -d -o ${spireuid} -g ${spiregid} -m 755 /spireagentroot/etc/spire/agent
RUN install -d -o ${spireuid} -g ${spiregid} -m 755 /spireagentroot/run/spire/agent/public
RUN install -d -o ${spireuid} -g ${spiregid} -m 755 /spireagentroot/var/lib/spire/agent
RUN xx-go --wrap
RUN set -e ; xx-apk --no-cache --update add build-base musl-dev libseccomp-dev
ENV CGO_ENABLED=1
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
if [ "$TARGETARCH" = "arm64" ]; then CC=aarch64-alpine-linux-musl; elif [ "$TARGETARCH" = "s390x" ]; then CC=s390x-alpine-linux-musl; fi && \
make build-static git_tag=$TAG git_dirty="" && \
for f in $(find bin -executable -type f); do xx-verify --static $f; done
make build-static && \
for f in $(find bin -executable -type f); do xx-verify $f; done
FROM --platform=${BUILDPLATFORM} scratch AS spire-base
COPY --link --from=builder --chown=root:root --chmod=755 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
WORKDIR /opt/spire
# Preparation environment for setting up directories
FROM alpine as prep-spire-server
RUN mkdir -p /spireroot/opt/spire/bin \
/spireroot/etc/spire/server \
/spireroot/run/spire/server/private \
/spireroot/tmp/spire-server/private \
/spireroot/var/lib/spire/server
FROM alpine as prep-spire-agent
RUN mkdir -p /spireroot/opt/spire/bin \
/spireroot/etc/spire/agent \
/spireroot/run/spire/agent/public \
/spireroot/tmp/spire-agent/public \
/spireroot/var/lib/spire/agent
# For users that wish to run SPIRE containers with a specific uid and gid, the
# spireuid and spiregid arguments are provided. The default paths that SPIRE
# will try to read from, write to, and create at runtime are given the
# corresponding file ownership/permissions at build time.
# A default non-root user is defined for SPIRE Server and the OIDC Discovery
# Provider. The SPIRE Agent image runs as root by default to facilitate the
# sharing of the agent socket in Kubernetes environments.
CMD []
COPY --link --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
# SPIRE Server
FROM spire-base AS spire-server
ARG spireuid=1000
ARG spiregid=1000
USER ${spireuid}:${spiregid}
ENTRYPOINT ["/opt/spire/bin/spire-server", "run"]
COPY --link --from=prep-spire-server --chown=${spireuid}:${spiregid} --chmod=755 /spireroot /
COPY --link --from=builder --chown=${spireuid}:${spiregid} --chmod=755 /spire/bin/static/spire-server /opt/spire/bin/
COPY --link --from=builder /spireserverroot /
COPY --link --from=builder /spire/bin/static/spire-server bin/
# SPIRE Agent
FROM spire-base AS spire-agent
ARG spireuid=0
ARG spiregid=0
USER ${spireuid}:${spiregid}
ENTRYPOINT ["/opt/spire/bin/spire-agent", "run"]
COPY --link --from=prep-spire-agent --chown=${spireuid}:${spiregid} --chmod=755 /spireroot /
COPY --link --from=builder --chown=${spireuid}:${spiregid} --chmod=755 /spire/bin/static/spire-agent /opt/spire/bin/
COPY --link --from=builder /spireagentroot /
COPY --link --from=builder /spire/bin/static/spire-agent bin/
# OIDC Discovery Provider
FROM spire-base AS oidc-discovery-provider
ARG spireuid=1000
ARG spiregid=1000
USER ${spireuid}:${spiregid}
ENTRYPOINT ["/opt/spire/bin/oidc-discovery-provider"]
COPY --link --from=builder --chown=${spireuid}:${spiregid} --chmod=755 /spire/bin/static/oidc-discovery-provider /opt/spire/bin/
COPY --link --from=builder /spire/bin/static/oidc-discovery-provider bin/

View File

@ -1,4 +1,4 @@
FROM ubuntu:24.04
FROM ubuntu:xenial
WORKDIR /spire
RUN apt-get update && apt-get -y install \
curl unzip git build-essential ca-certificates libssl-dev

View File

@ -31,9 +31,9 @@ This section of the document can and should be updated as the above consideratio
### Changes in Maintainership
SPIRE maintainers are appointed according to the [process described in the governance document][2]. Maintainers may voluntarily step down at any time. Unseating a maintainer against their will requires a unanimous vote except the unseated.
SPIRE maintainers are appointed according to the [process described in the governance document][2]. Maintainers may voluntarily step down at any time. Unseating a maintainer against their will requires a unanimous vote with the exception of the unseated.
Unseating a maintainer is an extraordinary circumstance. A process to do so is necessary, but its use is not intended. Careful consideration should be made when voting in a new maintainer, particularly in validating that they pledge to uphold the terms of this document. To ensure that these decisions are not taken lightly, and to maintain long term project stability and foresight, no more than one maintainer can be involuntarily unseated in any given nine-month period.
Unseating a maintainer is an extraordinary circumstance. A process to do so is necessary, but its use is not intended. Careful consideration should be made when voting in a new maintainer, particularly in validating that they pledge to uphold the terms of this document. To ensure that these decisions are not taken lightly, and to maintain long term project stability and foresight, no more than one maintainer can be involuntarily unseated in any given nine month period.
The CNCF MUST be notified of any changes in maintainership via the CNCF Service Desk.
@ -103,7 +103,7 @@ This is a very important aspect of SPIRE maintainership. Adoption and contributi
## Product Management and Roadmap Curation
In addition to the maintainer seats, the SPIRE project designates one product manager seat. While maintainers strive to ensure that project development and direction is a function of community needs, and interact with end users and contributors on a daily basis, the product manager works to clarify user needs by gathering additional information and context. This includes, but is not limited to, conducting user research and field-testing to better inform maintainers, and communicating project development information to the community.
In addition to the maintainer seats, the SPIRE project designates one product manager seat. While maintainers strive to ensure that project development and direction is a function of community needs, and interact with end users and contributors on a daily basis, the product manager works to clarify user needs by gathering additional information and context. This includes, but is not limited to, conducting user research and field testing to better inform maintainers, and communicating project development information to the community.
Maintainers are expected to have heavy participation in the community, but it may be impractical to dedicate themselves to gathering and analyzing community feedback and end-user pain points. Based on data collection, the role of the product manager is intended to aid maintainers to validate the desirability, feasibility, and viability of efforts to help drive project direction and priorities in long term planning.

View File

@ -32,7 +32,6 @@ help:
@echo " $(cyan)race-test$(reset) - run unit tests with race detection"
@echo " $(cyan)integration$(reset) - run integration tests (requires Docker images)"
@echo " support 'SUITES' variable for executing specific tests"
@echo " and 'IGNORE_SUITES' variable for ignoring tests"
@echo " e.g. SUITES='suites/join-token suites/k8s' make integration"
@echo " $(cyan)integration-windows$(reset) - run integration tests for windows (requires Docker images)"
@echo " support 'SUITES' variable for executing specific tests"
@ -104,8 +103,6 @@ else
$(error unsupported ARCH: $(arch1))
endif
ignore_suites := $(IGNORE_SUITES)
############################################################################
# Docker TLS detection for buildx
############################################################################
@ -139,14 +136,15 @@ endif
go_path := PATH="$(go_bin_dir):$(PATH)"
golangci_lint_version := $(shell awk '/golangci-lint/{print $$2}' .spire-tool-versions)
golangci_lint_version = v1.54.1
golangci_lint_dir = $(build_dir)/golangci_lint/$(golangci_lint_version)
golangci_lint_bin = $(golangci_lint_dir)/golangci-lint
golangci_lint_cache = $(golangci_lint_dir)/cache
markdown_lint_version := $(shell awk '/markdown_lint/{print $$2}' .spire-tool-versions)
markdown_lint_version = v0.35.0
markdown_lint_image = ghcr.io/igorshubovych/markdownlint-cli:$(markdown_lint_version)
protoc_version := $(shell awk '/protoc/{print $$2}' .spire-tool-versions)
protoc_version = 3.20.1
ifeq ($(os1),windows)
protoc_url = https://github.com/protocolbuffers/protobuf/releases/download/v$(protoc_version)/protoc-$(protoc_version)-win64.zip
else ifeq ($(arch2),arm64)
@ -166,7 +164,7 @@ protoc_gen_go_base_dir := $(build_dir)/protoc-gen-go
protoc_gen_go_dir := $(protoc_gen_go_base_dir)/$(protoc_gen_go_version)-go$(go_version)
protoc_gen_go_bin := $(protoc_gen_go_dir)/protoc-gen-go
protoc_gen_go_grpc_version := v1.3.0
protoc_gen_go_grpc_version := v1.1.0
protoc_gen_go_grpc_base_dir := $(build_dir)/protoc-gen-go-grpc
protoc_gen_go_grpc_dir := $(protoc_gen_go_grpc_base_dir)/$(protoc_gen_go_grpc_version)-go$(go_version)
protoc_gen_go_grpc_bin := $(protoc_gen_go_grpc_dir)/protoc-gen-go-grpc
@ -286,6 +284,7 @@ bin/static/%: cmd/% FORCE | go-check
$(E)$(go_build_static) $@$(exe) ./$<
bin/static/%: support/% FORCE | go-check
@echo Building $@
$(E)$(go_build_static) $@$(exe) ./$<
#############################################################################
@ -312,11 +311,11 @@ integration:
ifeq ($(os1), windows)
$(error Integration tests are not supported on windows)
else
$(E)$(go_path) IGNORE_SUITES='$(ignore_suites)' ./test/integration/test.sh $(SUITES)
$(E)$(go_path) ./test/integration/test.sh $(SUITES)
endif
integration-windows:
$(E)$(go_path) IGNORE_SUITES='$(ignore_suites)' ./test/integration/test-windows.sh $(SUITES)
$(E)./test/integration/test-windows.sh $(SUITES)
#############################################################################
# Docker Images
@ -338,7 +337,6 @@ $1: $3 container-builder
$(E)docker buildx build \
--platform $(PLATFORMS) \
--build-arg goversion=$(go_version) \
--build-arg TAG=$(TAG) \
--target $2 \
-o type=oci,dest=$2-image.tar \
-f $3 \
@ -404,11 +402,8 @@ endif
lint: lint-code lint-md
lint-code: | go-check
$(E)mkdir -p $(golangci_lint_cache)
$(E)$(go_path) GOLANGCI_LINT_CACHE="$(golangci_lint_cache)" \
go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$(golangci_lint_version) \
run --max-issues-per-linter=0 --max-same-issues=0 ./...
lint-code: $(golangci_lint_bin)
$(E)PATH="$(go_bin_dir):$(PATH)" GOLANGCI_LINT_CACHE="$(golangci_lint_cache)" $(golangci_lint_bin) run ./...
lint-md:
$(E)docker run --rm -v "$(DIR):/workdir" $(markdown_lint_image) "**/*.md"
@ -512,7 +507,7 @@ endif
go-bin-path: go-check
@echo "$(go_bin_dir):${PATH}"
install-toolchain: install-protoc install-protoc-gen-go | go-check
install-toolchain: install-protoc install-golangci-lint install-protoc-gen-go install-protoc-gen-doc | go-check
install-protoc: $(protoc_bin)
@ -522,6 +517,15 @@ $(protoc_bin):
$(E)mkdir -p $(protoc_dir)
$(E)curl -sSfL $(protoc_url) -o $(build_dir)/tmp.zip; unzip -q -d $(protoc_dir) $(build_dir)/tmp.zip; rm $(build_dir)/tmp.zip
install-golangci-lint: $(golangci_lint_bin)
$(golangci_lint_bin): | go-check
@echo "Installing golangci-lint $(golangci_lint_version)..."
$(E)rm -rf $(dir $(golangci_lint_dir))
$(E)mkdir -p $(golangci_lint_dir)
$(E)mkdir -p $(golangci_lint_cache)
$(E)GOBIN=$(golangci_lint_dir) $(go_path) go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(golangci_lint_version)
install-protoc-gen-go: $(protoc_gen_go_bin)
$(protoc_gen_go_bin): | go-check

View File

@ -3,6 +3,7 @@
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/3303/badge)](https://bestpractices.coreinfrastructure.org/projects/3303)
[![Build Status](https://github.com/spiffe/spire/actions/workflows/pr_build.yaml/badge.svg)](https://github.com/spiffe/spire/actions/workflows/pr_build.yaml)
[![Go Report Card](https://goreportcard.com/badge/github.com/spiffe/spire)](https://goreportcard.com/report/github.com/spiffe/spire)
[![Slack Status](https://slack.spiffe.io/badge.svg)](https://slack.spiffe.io)
[![Production Phase](https://img.shields.io/badge/SPIFFE-Prod-green.svg?logoWidth=18&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHJvbGU9ImltZyIgdmlld0JveD0iMC4xMSAxLjg2IDM1OC4yOCAzNTguMjgiPjxzdHlsZT5zdmcge2VuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMzYwIDM2MH08L3N0eWxlPjxzdHlsZT4uc3QyLC5zdDN7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojYmNkOTE4fS5zdDN7ZmlsbDojMDRiZGQ5fTwvc3R5bGU+PGcgaWQ9IkxPR08iPjxwYXRoIGQ9Ik0xMi4xIDguOWgyOC4zYzIuNyAwIDUgMi4yIDUgNXYyOC4zYzAgMi43LTIuMiA1LTUgNUgxMi4xYy0yLjcgMC01LTIuMi01LTVWMTMuOWMuMS0yLjcgMi4zLTUgNS01eiIgY2xhc3M9InN0MiIvPjxwYXRoIGQ9Ik04OC43IDguOWgyNThjMi43IDAgNSAyLjIgNSA1djI4LjNjMCAyLjctMi4yIDUtNSA1aC0yNThjLTIuNyAwLTUtMi4yLTUtNVYxMy45YzAtMi43IDIuMi01IDUtNXoiIGNsYXNzPSJzdDMiLz48cGF0aCBkPSJNMzQ2LjcgODUuNWgtMjguM2MtMi43IDAtNSAyLjItNSA1djI4LjNjMCAyLjggMi4yIDUgNSA1aDI4LjNjMi43IDAgNS0yLjIgNS01VjkwLjVjMC0yLjgtMi4zLTUtNS01eiIgY2xhc3M9InN0MiIvPjxwYXRoIGQ9Ik0xOTMuNiA4NS41SDEyLjFjLTIuNyAwLTUgMi4zLTUgNXYyOC4zYzAgMi43IDIuMiA1IDUgNWgxODEuNWMyLjcgMCA1LTIuMiA1LTVWOTAuNWMwLTIuOC0yLjItNS01LTV6IiBjbGFzcz0ic3QzIi8+PHBhdGggZD0iTTI3MC4yIDg1LjVoLTI4LjNjLTIuNyAwLTUgMi4yLTUgNXYyOC4zYzAgMi44IDIuMiA1IDUgNWgyOC4zYzIuNyAwIDUtMi4yIDUtNVY5MC41Yy0uMS0yLjgtMi4zLTUtNS01eiIgY2xhc3M9InN0MiIvPjxwYXRoIGQ9Ik0yNzAuMiAxNjJIODguN2MtMi43IDAtNSAyLjItNSA1djI4LjNjMCAyLjcgMi4yIDUgNSA1aDE4MS41YzIuNyAwIDUtMi4yIDUtNVYxNjdjLS4xLTIuOC0yLjMtNS01LTV6IiBjbGFzcz0ic3QzIi8+PHBhdGggZD0iTTM0Ni43IDE2MmgtMjguM2MtMi43IDAtNSAyLjItNSA1djI4LjNjMCAyLjggMi4yIDUgNSA1aDI4LjNjMi43IDAgNS0yLjIgNS01VjE2N2MwLTIuOC0yLjMtNS01LTV6bS0zMDYuMyAwSDEyLjFjLTIuNyAwLTUgMi4yLTUgNXYyOC4zYzAgMi44IDIuMiA1IDUgNWgyOC4zYzIuNyAwIDUtMi4yIDUtNVYxNjdjMC0yLjgtMi4yLTUtNS01em0tMjguMyA3Ni41aDI4LjNjMi43IDAgNSAyLjIgNSA1djI4LjNjMCAyLjctMi4yIDUtNSA1SDEyLjFjLTIuNyAwLTUtMi4yLTUtNXYtMjguM2MuMS0yLjcgMi4zLTUgNS01eiIgY2xhc3M9InN0MiIvPjxwYXRoIGQ9Ik0xNjUuMiAyMzguNWgxODEuNWMyLjcgMCA1IDIuMiA1IDV2MjguM2MwIDIuNy0yLjIgNS01IDVIMTY1LjJjLTIuNyAwLTUtMi4yLTUtNXYtMjguM2MwLTIuNyAyLjItNSA1LTV6IiBjbGFzcz0ic3QzIi8+PHBhdGggZD0iTTg4LjcgMjM4LjVIMTE3YzIuNyAwIDUgMi4yIDUgNXYyOC4zYzAgMi43LTIuMiA1LTUgNUg4OC43Yy0yLjcgMC01LTIuMi01LTV2LTI4LjNjMC0yLjcgMi4yLTUgNS01em0yNTggNzYuN2gtMjguM2MtMi43IDAtNSAyLjItNSA1djI4LjNjMCAyLjggMi4yIDUgNSA1aDI4LjNjMi43IDAgNS0yLjIgNS01di0yOC4zYzAtMi44LTIuMy01LTUtNXoiIGNsYXNzPSJzdDIiLz48cGF0aCBkPSJNMjcwLjIgMzE1LjJoLTI1OGMtMi43IDAtNSAyLjItNSA1djI4LjNjMCAyLjcgMi4yIDUgNSA1aDI1OGMyLjcgMCA1LTIuMiA1LTV2LTI4LjNjLS4xLTIuOC0yLjMtNS01LTV6IiBjbGFzcz0ic3QzIi8+PC9nPjwvc3ZnPg==)](https://github.com/spiffe/spiffe/blob/main/MATURITY.md#production)
SPIRE (the [SPIFFE](https://github.com/spiffe/spiffe) Runtime Environment) is a toolchain of APIs for establishing trust between software systems across a wide variety of hosting platforms. SPIRE exposes the [SPIFFE Workload API](https://github.com/spiffe/go-spiffe/blob/main/v2/proto/spiffe/workload/workload.proto), which can attest running software systems and issue [SPIFFE IDs](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE-ID.md) and [SVID](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE-ID.md)s to them. This in turn allows two workloads to establish trust between each other, for example by establishing an mTLS connection or by signing and verifying a JWT token. SPIRE can also enable workloads to securely authenticate to a secret store, a database, or a cloud provider service.
@ -61,8 +62,8 @@ The SPIFFE community maintains the SPIRE project. Information on the various SIG
A third party security firm ([Cure53](https://cure53.de/)) completed a security audit of SPIFFE and SPIRE in February of 2021. Additionally, the [CNCF Technical Advisory Group for Security](https://github.com/cncf/tag-security) conducted two assessments on SPIFFE and SPIRE in 2018 and 2020. Please find the reports and supporting material, including the threat model exercise results, below.
- [Cure53 Security Audit Report](doc/cure53-report.pdf)
- [SIG-Security SPIFFE/SPIRE Security Assessment: summary](https://github.com/cncf/sig-security/tree/main/community/assessments/projects/spiffe-spire)
- [SIG-Security SPIFFE/SPIRE Security Assessment: full assessment](https://github.com/cncf/sig-security/blob/main/community/assessments/projects/spiffe-spire/self-assessment.md)
- [SIG-Security SPIFFE/SPIRE Security Assessment: summary](https://github.com/cncf/sig-security/tree/main/assessments/projects/spiffe-spire)
- [SIG-Security SPIFFE/SPIRE Security Assessment: full assessment](https://github.com/cncf/sig-security/blob/main/assessments/projects/spiffe-spire/self-assessment.md)
- [Scrutinizing SPIRE to Sensibly Strengthen SPIFFE Security](https://blog.spiffe.io/scrutinizing-spire-security-9c82ba542019)
### Reporting Security Vulnerabilities

View File

@ -1,6 +1,6 @@
# Release and Branch Management
The SPIRE project maintains active support for both the current and the previous minor versions. All active development occurs in the `main` branch. Version branches are used for patch releases of the previous minor version when necessary.
The SPIRE project maintains active support for both the current and the previous major versions. All active development occurs in the `main` branch. Version branches are used for minor releases of the previous major version when necessary.
## Version Branches
@ -14,7 +14,7 @@ The base commit of the release branch is based on the type of release being gene
When a bug is discovered in the latest release that also affects releases of the prior minor version, it is necessary to backport the fix.
Once the version branch is created, the patch is either cherry-picked or backported into a PR against the version branch. The version branch is maintained via the same process as the main branch, including PR approval process etc.
Once the version branch is created, the patch is either cherry picked or backported into a PR against the version branch. The version branch is maintained via the same process as the main branch, including PR approval process etc.
Ensure that the CHANGELOG is updated in both `main` and the version branch to reflect the new release.
@ -59,7 +59,7 @@ The following steps must be completed by the primary on-call maintainer to perfo
* If the build fails, or anything unusual is encountered, abort the release.
* Ensure that the GitHub release, container images, and release artifacts are deleted/rolled back if necessary.
* Visit the releases page on GitHub, copy the release notes, click edit and paste them back in. This works around a GitHub Markdown rendering bug that you will notice before completing this task.
* Cut new SDK releases (see [SDK Releases](#sdk-releases)).
* Create Git tags (not annotated) with the name `vX.Y.Z` in the [spire-api-sdk](https://github.com/spiffe/spire-api-sdk) and [spire-plugin-sdk](https://github.com/spiffe/spire-plugin-sdk) repositories for the HEAD commit of the main branch.
* Open a PR targeted for the main branch with the following changes:
* Cherry-pick of the changelog commit from the latest release so that the changelog on the main branch contains all the release notes.
* Bump the SPIRE version to the next projected version. As for determining the next projected version, the project generally releases three patch releases per minor release cycle (e.g. `vX.Y.[0-3]`), not including dedicated security releases. The version needs to be updated in the following places:
@ -75,27 +75,3 @@ The following steps must be completed by the primary on-call maintainer to perfo
* PRs to update spiffe.io and spire-examples repo to the latest major version must be merged.
* Ensure that the PRs have been updated to use the version tag instead of the commit sha.
### SDK Releases
SPIRE has two SDK repositories:
* [API SDK](https://github.com/spiffe/spire-api-sdk)
* [Plugin SDK](https://github.com/spiffe/spire-plugin-sdk)
SPIRE consumes these SDKs using pseudo-versions from the `next` branch in each SDK repository. This allows unreleased changes to be reviewed, merged, and consumed by SPIRE.
These SDKs need to be released with each SPIRE release.
SDK releases take place using tagged commits from the `main` branch in each repository. When cutting a new release, the `main` branch needs to be prepared with any previously unreleased changes that are part of the new release.
To create a release for an SDK, perform the following steps:
1. Review the diff between `next` and `main`.
1. Determine the commits in `next` that are missing from `main`, in other words, commits containing features that were under development that are now publicly available through the new SPIRE release (e.g. API or plugin interface additions).
1. Cherry-pick those commits, if any, into `main`.
1. Create a git tag (not annotated) with the name `vX.Y.Z`, corresponding to the SPIRE release version, for the `HEAD` commit of the main branch.
1. Push the `vX.Y.Z` tag to Github.
> [!WARNING]
> Extra care should be taken to ensure that the tagged commit is correct before pushing. Once it has been pushed, anyone running `go get <SDK module>@latest` will cause the repository to be pulled into the Go module cache at that cache. Changing it afterwards is not without consequence.

View File

@ -1,4 +1,5 @@
//go:build !windows
// +build !windows
package api

View File

@ -15,9 +15,9 @@ import (
"github.com/mitchellh/cli"
"github.com/spiffe/go-spiffe/v2/proto/spiffe/workload"
"github.com/spiffe/go-spiffe/v2/spiffeid"
"github.com/spiffe/spire/cmd/spire-server/cli/common"
commoncli "github.com/spiffe/spire/pkg/common/cli"
"github.com/spiffe/spire/pkg/common/x509util"
"github.com/spiffe/spire/test/clitest"
"github.com/spiffe/spire/test/fakes/fakeworkloadapi"
"github.com/spiffe/spire/test/spiretest"
"github.com/spiffe/spire/test/testca"
@ -416,10 +416,9 @@ func TestValidateJWTCommand(t *testing.T) {
Claims: &structpb.Struct{
Fields: map[string]*structpb.Value{
"aud": {
Kind: &structpb.Value_ListValue{
ListValue: &structpb.ListValue{
Values: []*structpb.Value{{Kind: &structpb.Value_StringValue{StringValue: "foo"}}},
},
Kind: &structpb.Value_ListValue{ListValue: &structpb.ListValue{
Values: []*structpb.Value{{Kind: &structpb.Value_StringValue{StringValue: "foo"}}},
},
},
},
},
@ -505,7 +504,7 @@ func setupTest(t *testing.T, newCmd func(env *commoncli.Env, clientMaker workloa
}, newWorkloadClient)
test := &apiTest{
addr: clitest.GetAddr(addr),
addr: common.GetAddr(addr),
stdin: stdin,
stdout: stdout,
stderr: stderr,
@ -539,7 +538,7 @@ func (s *apiTest) afterTest(t *testing.T) {
}
func (s *apiTest) args(extra ...string) []string {
return append([]string{clitest.AddrArg, s.addr}, extra...)
return append([]string{common.AddrArg, s.addr}, extra...)
}
func assertOutputBasedOnFormat(t *testing.T, format, stdoutString, expectedStdoutJSON string, expectedStdoutPretty ...string) {

View File

@ -1,4 +1,5 @@
//go:build windows
// +build windows
package api

View File

@ -28,7 +28,7 @@ func newWorkloadClient(ctx context.Context, addr net.Addr, timeout time.Duration
if err != nil {
return nil, err
}
conn, err := util.NewGRPCClient(target)
conn, err := util.GRPCDialContext(ctx, target)
if err != nil {
return nil, err
}

View File

@ -78,7 +78,7 @@ func (c *fetchJWTCommand) fetchJWTBundles(ctx context.Context, client *workloadC
return stream.Recv()
}
func printPrettyResult(env *commoncli.Env, results ...any) error {
func printPrettyResult(env *commoncli.Env, results ...interface{}) error {
svidResp, ok := results[0].(*workload.JWTSVIDResponse)
if !ok {
env.Println(cliprinter.ErrInternalCustomPrettyFunc.Error())

View File

@ -151,7 +151,7 @@ func (c *fetchX509Command) writeFile(filename string, data []byte) error {
return diskutil.WritePubliclyReadableFile(filename, data)
}
func (c *fetchX509Command) prettyPrintFetchX509(env *commoncli.Env, results ...any) error {
func (c *fetchX509Command) prettyPrintFetchX509(env *commoncli.Env, results ...interface{}) error {
resp, ok := results[0].(*workload.X509SVIDResponse)
if !ok {
return cliprinter.ErrInternalCustomPrettyFunc

View File

@ -76,7 +76,7 @@ func (c *validateJWTCommand) validateJWTSVID(ctx context.Context, client *worklo
return resp, nil
}
func prettyPrintValidate(env *commoncli.Env, results ...any) error {
func prettyPrintValidate(env *commoncli.Env, results ...interface{}) error {
resp, ok := results[0].(*workload.ValidateJWTSVIDResponse)
if !ok {
return cliprinter.ErrInternalCustomPrettyFunc

View File

@ -1,4 +1,5 @@
//go:build !windows
// +build !windows
package common

View File

@ -1,4 +1,5 @@
//go:build windows
// +build windows
package common

View File

@ -1,10 +1,9 @@
//go:build !windows
// +build !windows
package common
const (
// DefaultSocketPath is the SPIRE agent's default socket path
DefaultSocketPath = "/tmp/spire-agent/public/api.sock"
// DefaultAdminSocketPath is the SPIRE agent's default admin socket path
DefaultAdminSocketPath = "/tmp/spire-agent/private/admin.sock"
)

View File

@ -1,10 +1,9 @@
//go:build windows
// +build windows
package common
const (
// DefaultNamedPipeName is the SPIRE agent's default named pipe name
DefaultNamedPipeName = "\\spire-agent\\public\\api"
// DefaultAdminNamedPipeName is the SPIRE agent's default admin named pipe name
DefaultAdminNamedPipeName = "\\spire-agent\\private\\admin"
)

View File

@ -79,7 +79,7 @@ func (c *healthCheckCommand) run() error {
if err != nil {
return err
}
conn, err := util.NewGRPCClient(target)
conn, err := util.GRPCDialContext(context.Background(), target)
if err != nil {
return err
}

View File

@ -1,4 +1,5 @@
//go:build !windows
// +build !windows
package healthcheck

View File

@ -1,4 +1,5 @@
//go:build !windows
// +build !windows
package healthcheck

View File

@ -1,4 +1,5 @@
//go:build windows
// +build windows
package healthcheck

View File

@ -1,4 +1,5 @@
//go:build windows
// +build windows
package healthcheck

View File

@ -2,11 +2,13 @@ package run
import (
"context"
"crypto/x509"
"errors"
"flag"
"fmt"
"io"
"net"
"net/http"
"net/url"
"os"
"os/signal"
@ -24,18 +26,18 @@ import (
"github.com/imdario/mergo"
"github.com/mitchellh/cli"
"github.com/sirupsen/logrus"
"github.com/spiffe/go-spiffe/v2/spiffeid"
"github.com/spiffe/spire/pkg/agent"
"github.com/spiffe/spire/pkg/agent/trustbundlesources"
"github.com/spiffe/spire/pkg/agent/workloadkey"
"github.com/spiffe/spire/pkg/common/bundleutil"
"github.com/spiffe/spire/pkg/common/catalog"
common_cli "github.com/spiffe/spire/pkg/common/cli"
"github.com/spiffe/spire/pkg/common/config"
"github.com/spiffe/spire/pkg/common/fflag"
"github.com/spiffe/spire/pkg/common/health"
"github.com/spiffe/spire/pkg/common/idutil"
"github.com/spiffe/spire/pkg/common/log"
"github.com/spiffe/spire/pkg/common/pemutil"
"github.com/spiffe/spire/pkg/common/telemetry"
"github.com/spiffe/spire/pkg/common/tlspolicy"
)
const (
@ -51,7 +53,8 @@ const (
defaultDefaultAllBundlesName = "ALL"
defaultDisableSPIFFECertValidation = false
minimumAvailabilityTarget = 24 * time.Hour
bundleFormatPEM = "pem"
bundleFormatSPIFFE = "spiffe"
)
// Config contains all available configurables, arranged by section
@ -67,7 +70,6 @@ type agentConfig struct {
DataDir string `hcl:"data_dir"`
AdminSocketPath string `hcl:"admin_socket_path"`
InsecureBootstrap bool `hcl:"insecure_bootstrap"`
RetryBootstrap *bool `hcl:"retry_bootstrap"`
JoinToken string `hcl:"join_token"`
LogFile string `hcl:"log_file"`
LogFormat string `hcl:"log_format"`
@ -80,14 +82,10 @@ type agentConfig struct {
WorkloadX509SVIDKeyType string `hcl:"workload_x509_svid_key_type"`
TrustBundleFormat string `hcl:"trust_bundle_format"`
TrustBundlePath string `hcl:"trust_bundle_path"`
TrustBundleUnixSocket string `hcl:"trust_bundle_unix_socket"`
TrustBundleURL string `hcl:"trust_bundle_url"`
TrustDomain string `hcl:"trust_domain"`
AllowUnauthenticatedVerifiers bool `hcl:"allow_unauthenticated_verifiers"`
AllowedForeignJWTClaims []string `hcl:"allowed_foreign_jwt_claims"`
AvailabilityTarget string `hcl:"availability_target"`
X509SVIDCacheMaxSize int `hcl:"x509_svid_cache_max_size"`
JWTSVIDCacheMaxSize int `hcl:"jwt_svid_cache_max_size"`
AuthorizedDelegates []string `hcl:"authorized_delegates"`
@ -112,13 +110,14 @@ type sdsConfig struct {
}
type experimentalConfig struct {
SyncInterval string `hcl:"sync_interval"`
NamedPipeName string `hcl:"named_pipe_name"`
AdminNamedPipeName string `hcl:"admin_named_pipe_name"`
UseSyncAuthorizedEntries *bool `hcl:"use_sync_authorized_entries"`
RequirePQKEM bool `hcl:"require_pq_kem"`
SyncInterval string `hcl:"sync_interval"`
NamedPipeName string `hcl:"named_pipe_name"`
AdminNamedPipeName string `hcl:"admin_named_pipe_name"`
Flags fflag.RawConfig `hcl:"feature_flags"`
UnusedKeyPositions map[string][]token.Pos `hcl:",unusedKeyPositions"`
X509SVIDCacheMaxSize int `hcl:"x509_svid_cache_max_size"`
}
type Command struct {
@ -234,21 +233,11 @@ func (c *agentConfig) validate() error {
return errors.New("trust_domain must be configured")
}
// If insecure_bootstrap is set, trust_bundle_path or trust_bundle_url cannot be set
// If trust_bundle_url is set, download the trust bundle using HTTP and parse it from memory
// If trust_bundle_path is set, parse the trust bundle file on disk
// Both cannot be set
// The trust bundle URL must start with HTTPS
if c.InsecureBootstrap {
switch {
case c.TrustBundleURL != "" && c.TrustBundlePath != "":
return errors.New("only one of insecure_bootstrap, trust_bundle_url, or trust_bundle_path can be specified, not the three options")
case c.TrustBundleURL != "":
return errors.New("only one of insecure_bootstrap or trust_bundle_url can be specified, not both")
case c.TrustBundlePath != "":
return errors.New("only one of insecure_bootstrap or trust_bundle_path can be specified, not both")
}
} else if c.TrustBundlePath == "" && c.TrustBundleURL == "" {
if c.TrustBundlePath == "" && c.TrustBundleURL == "" && !c.InsecureBootstrap {
return errors.New("trust_bundle_path or trust_bundle_url must be configured unless insecure_bootstrap is set")
}
@ -256,32 +245,16 @@ func (c *agentConfig) validate() error {
return errors.New("only one of trust_bundle_url or trust_bundle_path can be specified, not both")
}
if c.TrustBundleFormat != trustbundlesources.BundleFormatPEM && c.TrustBundleFormat != trustbundlesources.BundleFormatSPIFFE {
return fmt.Errorf("invalid value for trust_bundle_format, expected %q or %q", trustbundlesources.BundleFormatPEM, trustbundlesources.BundleFormatSPIFFE)
if c.TrustBundleFormat != bundleFormatPEM && c.TrustBundleFormat != bundleFormatSPIFFE {
return fmt.Errorf("invalid value for trust_bundle_format, expected %q or %q", bundleFormatPEM, bundleFormatSPIFFE)
}
if c.TrustBundleUnixSocket != "" && c.TrustBundleURL == "" {
return errors.New("if trust_bundle_unix_socket is specified, so must be trust_bundle_url")
}
if c.TrustBundleURL != "" {
u, err := url.Parse(c.TrustBundleURL)
if err != nil {
return fmt.Errorf("unable to parse trust bundle URL: %w", err)
}
if c.TrustBundleUnixSocket != "" {
if u.Scheme != "http" {
return errors.New("trust bundle URL must start with http:// when used with trust bundle unix socket")
}
params := u.Query()
for key := range params {
if strings.HasPrefix(key, "spiffe-") {
return errors.New("trust_bundle_url query params can not start with spiffe-")
}
if strings.HasPrefix(key, "spire-") {
return errors.New("trust_bundle_url query params can not start with spire-")
}
}
} else if u.Scheme != "https" {
if u.Scheme != "https" {
return errors.New("trust bundle URL must start with https://")
}
}
@ -315,7 +288,7 @@ func ParseFile(path string, expandEnv bool) (*Config, error) {
// If envTemplate flag is passed, substitute $VARIABLES in configuration file
if expandEnv {
data = config.ExpandEnv(data)
data = os.ExpandEnv(data)
}
if err := hcl.Decode(&c, data); err != nil {
@ -329,7 +302,6 @@ func parseFlags(name string, args []string, output io.Writer) (*agentConfig, err
flags := flag.NewFlagSet(name, flag.ContinueOnError)
flags.SetOutput(output)
c := &agentConfig{}
retryBootstrap := false
flags.StringVar(&c.ConfigPath, "config", defaultConfigPath, "Path to a SPIRE config file")
flags.StringVar(&c.DataDir, "dataDir", "", "A directory the agent can use for its runtime data")
@ -343,10 +315,9 @@ func parseFlags(name string, args []string, output io.Writer) (*agentConfig, err
flags.StringVar(&c.TrustDomain, "trustDomain", "", "The trust domain that this agent belongs to")
flags.StringVar(&c.TrustBundlePath, "trustBundle", "", "Path to the SPIRE server CA bundle")
flags.StringVar(&c.TrustBundleURL, "trustBundleUrl", "", "URL to download the SPIRE server CA bundle")
flags.StringVar(&c.TrustBundleFormat, "trustBundleFormat", "", fmt.Sprintf("Format of the bootstrap trust bundle, %q or %q", trustbundlesources.BundleFormatPEM, trustbundlesources.BundleFormatSPIFFE))
flags.StringVar(&c.TrustBundleFormat, "trustBundleFormat", "", fmt.Sprintf("Format of the bootstrap trust bundle, %q or %q", bundleFormatPEM, bundleFormatSPIFFE))
flags.BoolVar(&c.AllowUnauthenticatedVerifiers, "allowUnauthenticatedVerifiers", false, "If true, the agent permits the retrieval of X509 certificate bundles by unregistered clients")
flags.BoolVar(&c.InsecureBootstrap, "insecureBootstrap", false, "If true, the agent bootstraps without verifying the server's identity")
flags.BoolVar(&retryBootstrap, "retryBootstrap", true, "If true, the agent retries bootstrap with backoff")
flags.BoolVar(&c.ExpandEnv, "expandEnv", false, "Expand environment variables in SPIRE config file")
c.addOSFlags(flags)
@ -356,12 +327,6 @@ func parseFlags(name string, args []string, output io.Writer) (*agentConfig, err
return nil, err
}
flags.Visit(func(f *flag.Flag) {
if f.Name == "retryBootstrap" {
c.RetryBootstrap = &retryBootstrap
}
})
return c, nil
}
@ -387,6 +352,87 @@ func mergeInput(fileInput *Config, cliInput *agentConfig) (*Config, error) {
return c, nil
}
func parseTrustBundle(bundleBytes []byte, trustBundleContentType string) ([]*x509.Certificate, error) {
switch trustBundleContentType {
case bundleFormatPEM:
bundle, err := pemutil.ParseCertificates(bundleBytes)
if err != nil {
return nil, err
}
return bundle, nil
case bundleFormatSPIFFE:
bundle, err := bundleutil.Unmarshal(spiffeid.TrustDomain{}, bundleBytes)
if err != nil {
return nil, fmt.Errorf("unable to parse SPIFFE trust bundle: %w", err)
}
return bundle.X509Authorities(), nil
}
return nil, fmt.Errorf("unknown trust bundle format: %s", trustBundleContentType)
}
func downloadTrustBundle(trustBundleURL string) ([]byte, error) {
// Download the trust bundle URL from the user specified URL
// We use gosec -- the annotation below will disable a security check that URLs are not tainted
/* #nosec G107 */
resp, err := http.Get(trustBundleURL)
if err != nil {
return nil, fmt.Errorf("unable to fetch trust bundle URL %s: %w", trustBundleURL, err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("error downloading trust bundle: %s", resp.Status)
}
pemBytes, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("unable to read from trust bundle URL %s: %w", trustBundleURL, err)
}
return pemBytes, nil
}
func setupTrustBundle(ac *agent.Config, c *Config) error {
// Either download the trust bundle if TrustBundleURL is set, or read it
// from disk if TrustBundlePath is set
ac.InsecureBootstrap = c.Agent.InsecureBootstrap
var bundleBytes []byte
var err error
switch {
case c.Agent.TrustBundleURL != "":
bundleBytes, err = downloadTrustBundle(c.Agent.TrustBundleURL)
if err != nil {
return err
}
case c.Agent.TrustBundlePath != "":
bundleBytes, err = loadTrustBundle(c.Agent.TrustBundlePath)
if err != nil {
return fmt.Errorf("could not parse trust bundle: %w", err)
}
default:
// If InsecureBootstrap is configured, the bundle is not required
if ac.InsecureBootstrap {
return nil
}
}
bundle, err := parseTrustBundle(bundleBytes, c.Agent.TrustBundleFormat)
if err != nil {
return err
}
if len(bundle) == 0 {
return errors.New("no certificates found in trust bundle")
}
ac.TrustBundle = bundle
return nil
}
func NewAgentConfig(c *Config, logOptions []log.Option, allowUnknownConfig bool) (*agent.Config, error) {
ac := &agent.Config{}
@ -402,6 +448,11 @@ func NewAgentConfig(c *Config, logOptions []log.Option, allowUnknownConfig bool)
}
}
if c.Agent.Experimental.X509SVIDCacheMaxSize < 0 {
return nil, errors.New("x509_svid_cache_max_size should not be negative")
}
ac.X509SVIDCacheMaxSize = c.Agent.Experimental.X509SVIDCacheMaxSize
serverHostPort := net.JoinHostPort(c.Agent.ServerAddress, strconv.Itoa(c.Agent.ServerPort))
ac.ServerAddress = fmt.Sprintf("dns:///%s", serverHostPort)
@ -414,8 +465,7 @@ func NewAgentConfig(c *Config, logOptions []log.Option, allowUnknownConfig bool)
}
var reopenableFile *log.ReopenableFile
if c.Agent.LogFile != "" {
var err error
reopenableFile, err = log.NewReopenableFile(c.Agent.LogFile)
reopenableFile, err := log.NewReopenableFile(c.Agent.LogFile)
if err != nil {
return nil, err
}
@ -431,28 +481,6 @@ func NewAgentConfig(c *Config, logOptions []log.Option, allowUnknownConfig bool)
ac.LogReopener = log.ReopenOnSignal(logger, reopenableFile)
}
ac.RetryBootstrap = true
if c.Agent.RetryBootstrap != nil {
ac.Log.Warn("The 'retry_bootstrap' configuration is deprecated. It will be removed in SPIRE 1.14. Please test without the flag before upgrading.")
ac.RetryBootstrap = *c.Agent.RetryBootstrap
}
ac.UseSyncAuthorizedEntries = true
if c.Agent.Experimental.UseSyncAuthorizedEntries != nil {
ac.Log.Warn("The 'use_sync_authorized_entries' configuration is deprecated. The option to disable it will be removed in SPIRE 1.13.")
ac.UseSyncAuthorizedEntries = *c.Agent.Experimental.UseSyncAuthorizedEntries
}
if c.Agent.X509SVIDCacheMaxSize < 0 {
return nil, errors.New("x509_svid_cache_max_size should not be negative")
}
ac.X509SVIDCacheMaxSize = c.Agent.X509SVIDCacheMaxSize
if c.Agent.JWTSVIDCacheMaxSize < 0 {
return nil, errors.New("jwt_svid_cache_max_size should not be negative")
}
ac.JWTSVIDCacheMaxSize = c.Agent.JWTSVIDCacheMaxSize
td, err := common_cli.ParseTrustDomain(c.Agent.TrustDomain, logger)
if err != nil {
return nil, err
@ -482,16 +510,11 @@ func NewAgentConfig(c *Config, logOptions []log.Option, allowUnknownConfig bool)
}
ac.DisableSPIFFECertValidation = c.Agent.SDS.DisableSPIFFECertValidation
ts := &trustbundlesources.Config{
InsecureBootstrap: c.Agent.InsecureBootstrap,
TrustBundleFormat: c.Agent.TrustBundleFormat,
TrustBundlePath: c.Agent.TrustBundlePath,
TrustBundleURL: c.Agent.TrustBundleURL,
TrustBundleUnixSocket: c.Agent.TrustBundleUnixSocket,
err = setupTrustBundle(ac, c)
if err != nil {
return nil, err
}
ac.TrustBundleSources = trustbundlesources.New(ts, ac.Log.WithField("Logger", "TrustBundleSources"))
ac.WorkloadKeyType = workloadkey.ECP256
if c.Agent.WorkloadX509SVIDKeyType != "" {
ac.WorkloadKeyType, err = workloadkey.KeyTypeFromString(c.Agent.WorkloadX509SVIDKeyType)
@ -531,23 +554,6 @@ func NewAgentConfig(c *Config, logOptions []log.Option, allowUnknownConfig bool)
ac.AuthorizedDelegates = c.Agent.AuthorizedDelegates
if c.Agent.AvailabilityTarget != "" {
t, err := time.ParseDuration(c.Agent.AvailabilityTarget)
if err != nil {
return nil, fmt.Errorf("unable to parse availability_target: %w", err)
}
if t < minimumAvailabilityTarget {
return nil, fmt.Errorf("availability_target must be at least %s", minimumAvailabilityTarget.String())
}
ac.AvailabilityTarget = t
}
ac.TLSPolicy = tlspolicy.Policy{
RequirePQKEM: c.Agent.Experimental.RequirePQKEM,
}
tlspolicy.LogPolicy(ac.TLSPolicy, log.NewHCLogAdapter(logger, "tlspolicy"))
if cmp.Diff(experimentalConfig{}, c.Agent.Experimental) != "" {
logger.Warn("Experimental features have been enabled. Please see doc/upgrading.md for upgrade and compatibility considerations for experimental features.")
}
@ -636,7 +642,7 @@ func defaultConfig() *Config {
DataDir: defaultDataDir,
LogLevel: defaultLogLevel,
LogFormat: log.DefaultFormat,
TrustBundleFormat: trustbundlesources.BundleFormatPEM,
TrustBundleFormat: bundleFormatPEM,
SDS: sdsConfig{
DefaultBundleName: defaultDefaultBundleName,
DefaultSVIDName: defaultDefaultSVIDName,
@ -649,3 +655,12 @@ func defaultConfig() *Config {
return c
}
func loadTrustBundle(path string) ([]byte, error) {
bundleBytes, err := os.ReadFile(path)
if err != nil {
return nil, err
}
return bundleBytes, nil
}

View File

@ -1,4 +1,5 @@
//go:build !windows
// +build !windows
package run

View File

@ -1,4 +1,5 @@
//go:build !windows
// +build !windows
package run
@ -6,7 +7,7 @@ import (
"bytes"
"fmt"
"os"
"path"
"syscall"
"testing"
"github.com/spiffe/spire/pkg/agent"
@ -16,7 +17,6 @@ import (
"github.com/spiffe/spire/pkg/common/log"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/sys/unix"
)
func TestCommand_Run(t *testing.T) {
@ -130,8 +130,8 @@ func TestCommand_Run(t *testing.T) {
}
if testCase.want.agentUdsDirCreated {
assert.DirExistsf(t, testAgentSocketDir, "spire-agent uds dir should be created")
currentUmask := unix.Umask(0)
assert.Equalf(t, currentUmask, 0o027, "spire-agent process should be created with 0027 umask")
currentUmask := syscall.Umask(0)
assert.Equalf(t, currentUmask, 0027, "spire-agent process should be created with 0027 umask")
} else {
assert.NoDirExistsf(t, testAgentSocketDir, "spire-agent uds dir should not be created")
}
@ -187,28 +187,28 @@ func TestParseConfigGood(t *testing.T) {
// Check for plugins configurations
expectedPluginConfigs := catalog.PluginConfigs{
{
Type: "plugin_type_agent",
Name: "plugin_name_agent",
Path: "./pluginAgentCmd",
Checksum: "pluginAgentChecksum",
DataSource: catalog.FixedData(data),
Disabled: false,
Type: "plugin_type_agent",
Name: "plugin_name_agent",
Path: "./pluginAgentCmd",
Checksum: "pluginAgentChecksum",
Data: data,
Disabled: false,
},
{
Type: "plugin_type_agent",
Name: "plugin_disabled",
Path: "./pluginAgentCmd",
Checksum: "pluginAgentChecksum",
DataSource: catalog.FixedData(data),
Disabled: true,
Type: "plugin_type_agent",
Name: "plugin_disabled",
Path: "./pluginAgentCmd",
Checksum: "pluginAgentChecksum",
Data: data,
Disabled: true,
},
{
Type: "plugin_type_agent",
Name: "plugin_enabled",
Path: "./pluginAgentCmd",
Checksum: "pluginAgentChecksum",
DataSource: catalog.FileData("plugin.conf"),
Disabled: false,
Type: "plugin_type_agent",
Name: "plugin_enabled",
Path: "./pluginAgentCmd",
Checksum: "pluginAgentChecksum",
Data: data,
Disabled: false,
},
}
@ -238,7 +238,7 @@ func mergeInputCasesOS() []mergeInputCase {
},
},
{
msg: "socket_path should be configurable by CLI flag",
msg: "socket_path should be configuable by CLI flag",
fileInput: func(c *Config) {},
cliInput: func(c *agentConfig) {
c.SocketPath = "foo"
@ -272,9 +272,7 @@ func mergeInputCasesOS() []mergeInputCase {
}
}
func newAgentConfigCasesOS(t *testing.T) []newAgentConfigCase {
testDir := t.TempDir()
func newAgentConfigCasesOS() []newAgentConfigCase {
return []newAgentConfigCase{
{
msg: "socket_path should be correctly configured",
@ -362,15 +360,5 @@ func newAgentConfigCasesOS(t *testing.T) []newAgentConfigCase {
require.Nil(t, c.AdminBindAddress)
},
},
{
msg: "log_file allows to reopen",
input: func(c *Config) {
c.Agent.LogFile = path.Join(testDir, "foo")
},
test: func(t *testing.T, c *agent.Config) {
require.NotNil(t, c.Log)
require.NotNil(t, c.LogReopener)
},
},
}
}

View File

@ -2,12 +2,13 @@ package run
import (
"io"
"net/http"
"net/http/httptest"
"os"
"path"
"path/filepath"
"strings"
"testing"
"time"
"github.com/hashicorp/hcl/hcl/ast"
"github.com/sirupsen/logrus"
@ -29,12 +30,124 @@ type mergeInputCase struct {
}
type newAgentConfigCase struct {
msg string
expectError bool
requireErrorPrefix string
input func(*Config)
logOptions func(t *testing.T) []log.Option
test func(*testing.T, *agent.Config)
msg string
expectError bool
input func(*Config)
logOptions func(t *testing.T) []log.Option
test func(*testing.T, *agent.Config)
}
func TestDownloadTrustBundle(t *testing.T) {
testTB, _ := os.ReadFile(path.Join(util.ProjectRoot(), "conf/agent/dummy_root_ca.crt"))
testTBSPIFFE := `{
"keys": [
{
"use": "x509-svid",
"kty": "EC",
"crv": "P-384",
"x": "WjB-nSGSxIYiznb84xu5WGDZj80nL7W1c3zf48Why0ma7Y7mCBKzfQkrgDguI4j0",
"y": "Z-0_tDH_r8gtOtLLrIpuMwWHoe4vbVBFte1vj6Xt6WeE8lXwcCvLs_mcmvPqVK9j",
"x5c": [
"MIIBzDCCAVOgAwIBAgIJAJM4DhRH0vmuMAoGCCqGSM49BAMEMB4xCzAJBgNVBAYTAlVTMQ8wDQYDVQQKDAZTUElGRkUwHhcNMTgwNTEzMTkzMzQ3WhcNMjMwNTEyMTkzMzQ3WjAeMQswCQYDVQQGEwJVUzEPMA0GA1UECgwGU1BJRkZFMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEWjB+nSGSxIYiznb84xu5WGDZj80nL7W1c3zf48Why0ma7Y7mCBKzfQkrgDguI4j0Z+0/tDH/r8gtOtLLrIpuMwWHoe4vbVBFte1vj6Xt6WeE8lXwcCvLs/mcmvPqVK9jo10wWzAdBgNVHQ4EFgQUh6XzV6LwNazA+GTEVOdu07o5yOgwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwGQYDVR0RBBIwEIYOc3BpZmZlOi8vbG9jYWwwCgYIKoZIzj0EAwQDZwAwZAIwE4Me13qMC9i6Fkx0h26y09QZIbuRqA9puLg9AeeAAyo5tBzRl1YL0KNEp02VKSYJAjBdeJvqjJ9wW55OGj1JQwDFD7kWeEB6oMlwPbI/5hEY3azJi16I0uN1JSYTSWGSqWc="
]
}
]
}`
cases := []struct {
msg string
status int
fileContents string
format string
expectDownloadError bool
expectParseError bool
}{
{
msg: "if URL is not found, should be an error",
status: http.StatusNotFound,
fileContents: "",
format: bundleFormatPEM,
expectDownloadError: true,
expectParseError: false,
},
{
msg: "if URL returns error 500, should be an error",
status: http.StatusInternalServerError,
fileContents: "",
format: bundleFormatPEM,
expectDownloadError: true,
expectParseError: false,
},
{
msg: "if file is not parseable, should be an error",
status: http.StatusOK,
fileContents: "NON PEM PARSEABLE TEXT HERE",
format: bundleFormatPEM,
expectDownloadError: false,
expectParseError: true,
},
{
msg: "if file is empty, should be an error",
status: http.StatusOK,
fileContents: "",
format: bundleFormatPEM,
expectDownloadError: false,
expectParseError: true,
},
{
msg: "if file is valid, should not be an error",
status: http.StatusOK,
fileContents: string(testTB),
format: bundleFormatPEM,
expectDownloadError: false,
expectParseError: false,
},
{
msg: "if file is not parseable, format is SPIFFE, should not be an error",
status: http.StatusOK,
fileContents: "[}",
format: bundleFormatSPIFFE,
expectDownloadError: false,
expectParseError: true,
},
{
msg: "if file is valid, format is SPIFFE, should not be an error",
status: http.StatusOK,
fileContents: testTBSPIFFE,
format: bundleFormatSPIFFE,
expectDownloadError: false,
expectParseError: false,
},
}
for _, testCase := range cases {
testCase := testCase
t.Run(testCase.msg, func(t *testing.T) {
testServer := httptest.NewServer(http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(testCase.status)
_, _ = io.WriteString(w, testCase.fileContents)
// if err != nil {
// return
// }
}))
defer testServer.Close()
bundleBytes, err := downloadTrustBundle(testServer.URL)
if testCase.expectDownloadError {
require.Error(t, err)
} else {
require.NoError(t, err)
_, err := parseTrustBundle(bundleBytes, testCase.format)
if testCase.expectParseError {
require.Error(t, err)
} else {
require.NoError(t, err)
}
}
})
}
}
func TestMergeInput(t *testing.T) {
@ -500,20 +613,12 @@ func TestMergeInput(t *testing.T) {
require.Equal(t, "bar", c.Agent.TrustDomain)
},
},
{
msg: "require_pq_kem should be configurable by file",
fileInput: func(c *Config) {
c.Agent.Experimental.RequirePQKEM = true
},
cliInput: func(c *agentConfig) {},
test: func(t *testing.T, c *Config) {
require.True(t, c.Agent.Experimental.RequirePQKEM)
},
},
}
cases = append(cases, mergeInputCasesOS()...)
for _, testCase := range cases {
testCase := testCase
fileInput := &Config{Agent: &agentConfig{}}
cliInput := &agentConfig{}
@ -566,39 +671,16 @@ func TestNewAgentConfig(t *testing.T) {
c.Agent.InsecureBootstrap = false
},
test: func(t *testing.T, c *agent.Config) {
require.False(t, c.TrustBundleSources.GetInsecureBootstrap())
require.False(t, c.InsecureBootstrap)
},
},
{
msg: "insecure_bootstrap should be correctly set to true",
input: func(c *Config) {
// in this case, remove trust_bundle_path provided by defaultValidConfig()
// because trust_bundle_path and insecure_bootstrap cannot be set at the same time
c.Agent.TrustBundlePath = ""
c.Agent.InsecureBootstrap = true
},
test: func(t *testing.T, c *agent.Config) {
require.True(t, c.TrustBundleSources.GetInsecureBootstrap())
},
},
{
msg: "retry_bootstrap should be correctly set to false",
input: func(c *Config) {
rb := false
c.Agent.RetryBootstrap = &rb
},
test: func(t *testing.T, c *agent.Config) {
require.False(t, c.RetryBootstrap)
},
},
{
msg: "retry_bootstrap should be correctly set to true",
input: func(c *Config) {
rb := true
c.Agent.RetryBootstrap = &rb
},
test: func(t *testing.T, c *agent.Config) {
require.True(t, c.RetryBootstrap)
require.True(t, c.InsecureBootstrap)
},
},
{
@ -648,9 +730,8 @@ func TestNewAgentConfig(t *testing.T) {
},
},
{
msg: "trust_bundle_path and trust_bundle_url cannot both be set",
expectError: true,
requireErrorPrefix: "only one of trust_bundle_url or trust_bundle_path can be specified, not both",
msg: "trust_bundle_path and trust_bundle_url cannot both be set",
expectError: true,
input: func(c *Config) {
c.Agent.TrustBundlePath = "foo"
c.Agent.TrustBundleURL = "foo2"
@ -660,111 +741,10 @@ func TestNewAgentConfig(t *testing.T) {
},
},
{
msg: "insecure_bootstrap and trust_bundle_path cannot both be set",
expectError: true,
requireErrorPrefix: "only one of insecure_bootstrap or trust_bundle_path can be specified, not both",
msg: "insecure_bootstrap and trust_bundle_url cannot both be set",
expectError: true,
input: func(c *Config) {
c.Agent.TrustBundlePath = "foo"
c.Agent.InsecureBootstrap = true
},
test: func(t *testing.T, c *agent.Config) {
require.Nil(t, c)
},
},
{
msg: "insecure_bootstrap and trust_bundle_url cannot both be set",
expectError: true,
requireErrorPrefix: "only one of insecure_bootstrap or trust_bundle_url can be specified, not both",
input: func(c *Config) {
// in this case, remove trust_bundle_path provided by defaultValidConfig()
c.Agent.TrustBundlePath = ""
c.Agent.TrustBundleURL = "foo"
c.Agent.InsecureBootstrap = true
},
test: func(t *testing.T, c *agent.Config) {
require.Nil(t, c)
},
},
{
msg: "insecure_bootstrap, trust_bundle_url, trust_bundle_path cannot be set at the same time",
expectError: true,
requireErrorPrefix: "only one of insecure_bootstrap, trust_bundle_url, or trust_bundle_path can be specified, not the three options",
input: func(c *Config) {
c.Agent.TrustBundlePath = "bar"
c.Agent.TrustBundleURL = "foo"
c.Agent.InsecureBootstrap = true
},
test: func(t *testing.T, c *agent.Config) {
require.Nil(t, c)
},
},
{
msg: "trust_bundle_path or trust_bundle_url must be configured unless insecure_bootstrap is set",
expectError: true,
requireErrorPrefix: "trust_bundle_path or trust_bundle_url must be configured unless insecure_bootstrap is set",
input: func(c *Config) {
// in this case, remove trust_bundle_path provided by defaultValidConfig()
c.Agent.TrustBundlePath = ""
c.Agent.TrustBundleURL = ""
c.Agent.InsecureBootstrap = false
},
test: func(t *testing.T, c *agent.Config) {
require.Nil(t, c)
},
},
{
msg: "trust_bundle_url must start with https://",
expectError: true,
requireErrorPrefix: "trust bundle URL must start with https://",
input: func(c *Config) {
// remove trust_bundle_path provided by defaultValidConfig()
c.Agent.TrustBundlePath = ""
c.Agent.TrustBundleURL = "foo.bar"
c.Agent.InsecureBootstrap = false
},
test: func(t *testing.T, c *agent.Config) {
require.Nil(t, c)
},
},
{
msg: "trust_bundle_url must start with http:// when unix socket",
expectError: true,
requireErrorPrefix: "trust bundle URL must start with http://",
input: func(c *Config) {
// remove trust_bundle_path provided by defaultValidConfig()
c.Agent.TrustBundlePath = ""
c.Agent.TrustBundleURL = "foo.bar"
c.Agent.TrustBundleUnixSocket = "foo.bar"
c.Agent.InsecureBootstrap = false
},
test: func(t *testing.T, c *agent.Config) {
require.Nil(t, c)
},
},
{
msg: "trust_bundle_url query params can not start with spiffe- when unix socket",
expectError: true,
requireErrorPrefix: "trust_bundle_url query params can not start with spiffe-",
input: func(c *Config) {
// remove trust_bundle_path provided by defaultValidConfig()
c.Agent.TrustBundlePath = ""
c.Agent.TrustBundleURL = "http://localhost/trustbundle?spiffe-test=foo"
c.Agent.TrustBundleUnixSocket = "foo.bar"
c.Agent.InsecureBootstrap = false
},
test: func(t *testing.T, c *agent.Config) {
require.Nil(t, c)
},
},
{
msg: "trust_bundle_url query params can not start with spire- when unix socket",
expectError: true,
requireErrorPrefix: "trust_bundle_url query params can not start with spire-",
input: func(c *Config) {
// remove trust_bundle_path provided by defaultValidConfig()
c.Agent.TrustBundlePath = ""
c.Agent.TrustBundleURL = "http://localhost/trustbundle?spire-test=foo"
c.Agent.TrustBundleUnixSocket = "foo.bar"
c.Agent.InsecureBootstrap = false
},
test: func(t *testing.T, c *agent.Config) {
@ -840,7 +820,7 @@ func TestNewAgentConfig(t *testing.T) {
{
msg: "x509_svid_cache_max_size is set",
input: func(c *Config) {
c.Agent.X509SVIDCacheMaxSize = 100
c.Agent.Experimental.X509SVIDCacheMaxSize = 100
},
test: func(t *testing.T, c *agent.Config) {
require.EqualValues(t, 100, c.X509SVIDCacheMaxSize)
@ -857,7 +837,7 @@ func TestNewAgentConfig(t *testing.T) {
{
msg: "x509_svid_cache_max_size is zero",
input: func(c *Config) {
c.Agent.X509SVIDCacheMaxSize = 0
c.Agent.Experimental.X509SVIDCacheMaxSize = 0
},
test: func(t *testing.T, c *agent.Config) {
require.EqualValues(t, 0, c.X509SVIDCacheMaxSize)
@ -867,7 +847,7 @@ func TestNewAgentConfig(t *testing.T) {
msg: "x509_svid_cache_max_size is negative",
expectError: true,
input: func(c *Config) {
c.Agent.X509SVIDCacheMaxSize = -10
c.Agent.Experimental.X509SVIDCacheMaxSize = -10
},
test: func(t *testing.T, c *agent.Config) {
require.Nil(t, c)
@ -918,7 +898,7 @@ func TestNewAgentConfig(t *testing.T) {
t.Cleanup(func() {
spiretest.AssertLogsContainEntries(t, hook.AllEntries(), []spiretest.LogEntry{
{
Data: map[string]any{"trust_domain": strings.Repeat("a", 256)},
Data: map[string]interface{}{"trust_domain": strings.Repeat("a", 256)},
Level: logrus.WarnLevel,
Message: "Configured trust domain name should be less than 255 characters to be " +
"SPIFFE compliant; a longer trust domain name may impact interoperability",
@ -933,45 +913,11 @@ func TestNewAgentConfig(t *testing.T) {
assert.NotNil(t, c)
},
},
{
msg: "availability_target parses a duration",
input: func(c *Config) {
c.Agent.AvailabilityTarget = "24h"
},
test: func(t *testing.T, c *agent.Config) {
require.EqualValues(t, 24*time.Hour, c.AvailabilityTarget)
},
},
{
msg: "availability_target is too short",
expectError: true,
input: func(c *Config) {
c.Agent.AvailabilityTarget = "1h"
},
test: func(t *testing.T, c *agent.Config) {
require.Nil(t, c)
},
},
{
msg: "require PQ KEM is disabled (default)",
input: func(c *Config) {},
test: func(t *testing.T, c *agent.Config) {
require.Equal(t, false, c.TLSPolicy.RequirePQKEM)
},
},
{
msg: "require PQ KEM is enabled",
input: func(c *Config) {
c.Agent.Experimental.RequirePQKEM = true
},
test: func(t *testing.T, c *agent.Config) {
require.Equal(t, true, c.TLSPolicy.RequirePQKEM)
},
},
}
cases = append(cases, newAgentConfigCasesOS(t)...)
cases = append(cases, newAgentConfigCasesOS()...)
for _, testCase := range cases {
testCase := testCase
input := defaultValidConfig()
testCase.input(input)
@ -985,9 +931,6 @@ func TestNewAgentConfig(t *testing.T) {
ac, err := NewAgentConfig(input, logOpts, false)
if testCase.expectError {
require.Error(t, err)
if testCase.requireErrorPrefix != "" {
spiretest.RequireErrorPrefix(t, err, testCase.requireErrorPrefix)
}
} else {
require.NoError(t, err)
}
@ -1134,6 +1077,8 @@ func TestWarnOnUnknownConfig(t *testing.T) {
}
for _, testCase := range cases {
testCase := testCase
c, err := ParseFile(filepath.Join(testFileDir, testCase.confFile), false)
require.NoError(t, err)

View File

@ -1,4 +1,5 @@
//go:build windows
// +build windows
package run

View File

@ -1,4 +1,5 @@
//go:build windows
// +build windows
package run
@ -173,28 +174,28 @@ func TestParseConfigGood(t *testing.T) {
// Check for plugins configurations
expectedPluginConfigs := catalog.PluginConfigs{
{
Type: "plugin_type_agent",
Name: "plugin_name_agent",
Path: "./pluginAgentCmd",
Checksum: "pluginAgentChecksum",
DataSource: catalog.FixedData(data),
Disabled: false,
Type: "plugin_type_agent",
Name: "plugin_name_agent",
Path: "./pluginAgentCmd",
Checksum: "pluginAgentChecksum",
Data: data,
Disabled: false,
},
{
Type: "plugin_type_agent",
Name: "plugin_disabled",
Path: ".\\pluginAgentCmd",
Checksum: "pluginAgentChecksum",
DataSource: catalog.FixedData(data),
Disabled: true,
Type: "plugin_type_agent",
Name: "plugin_disabled",
Path: ".\\pluginAgentCmd",
Checksum: "pluginAgentChecksum",
Data: data,
Disabled: true,
},
{
Type: "plugin_type_agent",
Name: "plugin_enabled",
Path: "c:/temp/pluginAgentCmd",
Checksum: "pluginAgentChecksum",
DataSource: catalog.FileData("plugin.conf"),
Disabled: false,
Type: "plugin_type_agent",
Name: "plugin_enabled",
Path: "c:/temp/pluginAgentCmd",
Checksum: "pluginAgentChecksum",
Data: data,
Disabled: false,
},
}
@ -224,7 +225,7 @@ func mergeInputCasesOS() []mergeInputCase {
},
},
{
msg: "named_pipe_name should be configurable by CLI flag",
msg: "named_pipe_name should be configuable by CLI flag",
fileInput: func(c *Config) {},
cliInput: func(c *agentConfig) {
c.Experimental.NamedPipeName = "foo"
@ -258,7 +259,7 @@ func mergeInputCasesOS() []mergeInputCase {
}
}
func newAgentConfigCasesOS(*testing.T) []newAgentConfigCase {
func newAgentConfigCasesOS() []newAgentConfigCase {
return []newAgentConfigCase{
{
msg: "named_pipe_name should be correctly configured",

View File

@ -1,4 +1,5 @@
//go:build !windows
// +build !windows
package agent_test
@ -14,14 +15,6 @@ var (
Path to the SPIRE Server API socket (default "/tmp/spire-server/private/api.sock")
`
listUsage = `Usage of agent list:
-attestationType string
Filter by attestation type, like join_token or x509pop.
-banned value
Filter based on string received, 'true': banned agents, 'false': not banned agents, other value will return all.
-canReattest value
Filter based on string received, 'true': agents that can reattest, 'false': agents that can't reattest, other value will return all.
-expiresBefore string
Filter by expiration time (format: "2006-01-02 15:04:05 -0700 -07")
-matchSelectorsOn string
The match mode used when filtering by selectors. Options: exact, any, superset and subset (default "superset")
-output value
@ -48,20 +41,8 @@ var (
The SPIFFE ID of the agent to evict (agent identity)
`
countUsage = `Usage of agent count:
-attestationType string
Filter by attestation type, like join_token or x509pop.
-banned value
Filter based on string received, 'true': banned agents, 'false': not banned agents, other value will return all.
-canReattest value
Filter based on string received, 'true': agents that can reattest, 'false': agents that can't reattest, other value will return all.
-expiresBefore string
Filter by expiration time (format: "2006-01-02 15:04:05 -0700 -07")
-matchSelectorsOn string
The match mode used when filtering by selectors. Options: exact, any, superset and subset (default "superset")
-output value
Desired output format (pretty, json); default: pretty.
-selector value
A colon-delimited type:value selector. Can be used more than once
-socketPath string
Path to the SPIRE Server API socket (default "/tmp/spire-server/private/api.sock")
`

View File

@ -12,8 +12,8 @@ import (
agentv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/agent/v1"
"github.com/spiffe/spire-api-sdk/proto/spire/api/types"
"github.com/spiffe/spire/cmd/spire-server/cli/agent"
"github.com/spiffe/spire/cmd/spire-server/cli/common"
commoncli "github.com/spiffe/spire/pkg/common/cli"
"github.com/spiffe/spire/test/clitest"
"github.com/spiffe/spire/test/spiretest"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
@ -92,13 +92,10 @@ func TestBan(t *testing.T) {
expectStderr: "Error: a SPIFFE ID is required\n",
},
{
name: "wrong UDS path",
args: []string{
clitest.AddrArg, clitest.AddrValue,
"-spiffeID", "spiffe://example.org/spire/agent/agent1",
},
name: "wrong UDS path",
args: []string{common.AddrArg, common.AddrValue},
expectReturnCode: 1,
expectStderr: "Error: " + clitest.AddrError,
expectStderr: common.AddrError,
},
{
name: "server error",
@ -155,13 +152,10 @@ func TestEvict(t *testing.T) {
expectedStderr: "Error: a SPIFFE ID is required\n",
},
{
name: "wrong UDS path",
args: []string{
clitest.AddrArg, clitest.AddrValue,
"-spiffeID", "spiffe://example.org/spire/agent/agent1",
},
name: "wrong UDS path",
args: []string{common.AddrArg, common.AddrValue},
expectedReturnCode: 1,
expectedStderr: "Error: " + clitest.AddrError,
expectedStderr: common.AddrError,
},
{
name: "server error",
@ -227,15 +221,9 @@ func TestCount(t *testing.T) {
},
{
name: "wrong UDS path",
args: []string{clitest.AddrArg, clitest.AddrValue},
args: []string{common.AddrArg, common.AddrValue},
expectedReturnCode: 1,
expectedStderr: "Error: " + clitest.AddrError,
},
{
name: "Count by expiresBefore: month out of range",
args: []string{"-expiresBefore", "2001-13-05"},
expectedReturnCode: 1,
expectedStderr: "Error: date is not valid: parsing time \"2001-13-05\": month out of range\n",
expectedStderr: common.AddrError,
},
} {
for _, format := range availableFormats {
@ -401,45 +389,6 @@ func TestList(t *testing.T) {
expectedStdoutPretty: "Found 1 attested agent:\n\nSPIFFE ID : spiffe://example.org/spire/agent/agent1",
expectedStdoutJSON: `{"agents":[{"id":{"trust_domain":"example.org","path":"/spire/agent/agent1"},"attestation_type":"","x509svid_serial_number":"","x509svid_expires_at":"0","selectors":[],"banned":false,"can_reattest":true}],"next_page_token":""}`,
},
{
name: "by expiresBefore",
args: []string{"-expiresBefore", "2000-01-01 15:04:05 -0700 -07"},
expectReq: &agentv1.ListAgentsRequest{
Filter: &agentv1.ListAgentsRequest_Filter{
ByExpiresBefore: "2000-01-01 15:04:05 -0700 -07",
},
PageSize: 1000,
},
existentAgents: testAgents,
expectedStdoutPretty: "Found 1 attested agent:\n\nSPIFFE ID : spiffe://example.org/spire/agent/agent1",
expectedStdoutJSON: `{"agents":[{"id":{"trust_domain":"example.org","path":"/spire/agent/agent1"},"attestation_type":"","x509svid_serial_number":"","x509svid_expires_at":"0","selectors":[],"banned":false,"can_reattest":true}],"next_page_token":""}`,
},
{
name: "by banned",
args: []string{"-banned", "true"},
expectReq: &agentv1.ListAgentsRequest{
Filter: &agentv1.ListAgentsRequest_Filter{
ByBanned: wrapperspb.Bool(true),
},
PageSize: 1000,
},
existentAgents: testAgentsWithBanned,
expectedStdoutPretty: "Found 1 attested agent:\n\nSPIFFE ID : spiffe://example.org/spire/agent/banned",
expectedStdoutJSON: `{"agents":[{"id":{"trust_domain":"example.org","path":"/spire/agent/banned"},"attestation_type":"","x509svid_serial_number":"","x509svid_expires_at":"0","selectors":[],"banned":true,"can_reattest":false}],"next_page_token":""}`,
},
{
name: "by canReattest",
args: []string{"-canReattest", "true"},
expectReq: &agentv1.ListAgentsRequest{
Filter: &agentv1.ListAgentsRequest_Filter{
ByCanReattest: wrapperspb.Bool(true),
},
PageSize: 1000,
},
existentAgents: testAgents,
expectedStdoutPretty: "Found 1 attested agent:\n\nSPIFFE ID : spiffe://example.org/spire/agent/agent1",
expectedStdoutJSON: `{"agents":[{"id":{"trust_domain":"example.org","path":"/spire/agent/agent1"},"attestation_type":"","x509svid_serial_number":"","x509svid_expires_at":"0","selectors":[],"banned":false,"can_reattest":true}],"next_page_token":""}`,
},
{
name: "List by selectors: Invalid matcher",
args: []string{"-selector", "foo:bar", "-selector", "bar:baz", "-matchSelectorsOn", "NO-MATCHER"},
@ -454,15 +403,9 @@ func TestList(t *testing.T) {
},
{
name: "wrong UDS path",
args: []string{clitest.AddrArg, clitest.AddrValue},
args: []string{common.AddrArg, common.AddrValue},
expectedReturnCode: 1,
expectedStderr: "Error: " + clitest.AddrError,
},
{
name: "List by expiresBefore: month out of range",
args: []string{"-expiresBefore", "2001-13-05"},
expectedReturnCode: 1,
expectedStderr: "Error: date is not valid: parsing time \"2001-13-05\": month out of range\n",
expectedStderr: common.AddrError,
},
} {
for _, format := range availableFormats {
@ -746,13 +689,10 @@ func TestShow(t *testing.T) {
expectedStderr: "Error: rpc error: code = Internal desc = internal server error\n",
},
{
name: "wrong UDS path",
args: []string{
clitest.AddrArg, clitest.AddrValue,
"-spiffeID", "spiffe://example.org/spire/agent/agent1",
},
name: "wrong UDS path",
args: []string{common.AddrArg, common.AddrValue},
expectedReturnCode: 1,
expectedStderr: "Error: " + clitest.AddrError,
expectedStderr: common.AddrError,
},
{
name: "show selectors",
@ -810,7 +750,7 @@ func setupTest(t *testing.T, newClient func(*commoncli.Env) cli.Command) *agentT
stdin: stdin,
stdout: stdout,
stderr: stderr,
args: []string{clitest.AddrArg, clitest.GetAddr(addr)},
args: []string{common.AddrArg, common.GetAddr(addr)},
server: server,
client: client,
}

View File

@ -1,4 +1,5 @@
//go:build windows
// +build windows
package agent_test
@ -14,14 +15,6 @@ var (
Desired output format (pretty, json); default: pretty.
`
listUsage = `Usage of agent list:
-attestationType string
Filter by attestation type, like join_token or x509pop.
-banned value
Filter based on string received, 'true': banned agents, 'false': not banned agents, other value will return all.
-canReattest value
Filter based on string received, 'true': agents that can reattest, 'false': agents that can't reattest, other value will return all.
-expiresBefore string
Filter by expiration time (format: "2006-01-02 15:04:05 -0700 -07")
-matchSelectorsOn string
The match mode used when filtering by selectors. Options: exact, any, superset and subset (default "superset")
-namedPipeName string
@ -48,22 +41,10 @@ var (
The SPIFFE ID of the agent to evict (agent identity)
`
countUsage = `Usage of agent count:
-attestationType string
Filter by attestation type, like join_token or x509pop.
-banned value
Filter based on string received, 'true': banned agents, 'false': not banned agents, other value will return all.
-canReattest value
Filter based on string received, 'true': agents that can reattest, 'false': agents that can't reattest, other value will return all.
-expiresBefore string
Filter by expiration time (format: "2006-01-02 15:04:05 -0700 -07")
-matchSelectorsOn string
The match mode used when filtering by selectors. Options: exact, any, superset and subset (default "superset")
-namedPipeName string
Pipe name of the SPIRE Server API named pipe (default "\\spire-server\\private\\api")
-output value
Desired output format (pretty, json); default: pretty.
-selector value
A colon-delimited type:value selector. Can be used more than once
`
showUsage = `Usage of agent show:
-namedPipeName string

View File

@ -67,7 +67,7 @@ func (c *banCommand) AppendFlags(fs *flag.FlagSet) {
cliprinter.AppendFlagWithCustomPretty(&c.printer, fs, c.env, prettyPrintBanResult)
}
func prettyPrintBanResult(env *commoncli.Env, _ ...any) error {
func prettyPrintBanResult(env *commoncli.Env, _ ...interface{}) error {
env.Println("Agent banned successfully")
return nil
}

View File

@ -1,43 +1,20 @@
package agent
import (
"context"
"errors"
"flag"
"fmt"
"time"
"github.com/mitchellh/cli"
agentv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/agent/v1"
"github.com/spiffe/spire-api-sdk/proto/spire/api/types"
"github.com/spiffe/spire/cmd/spire-server/util"
commoncli "github.com/spiffe/spire/pkg/common/cli"
"github.com/spiffe/spire/pkg/common/cliprinter"
"google.golang.org/protobuf/types/known/wrapperspb"
"golang.org/x/net/context"
)
type countCommand struct {
// Type and value are delimited by a colon (:)
// ex. "unix:uid:1000" or "spiffe_id:spiffe://example.org/foo"
selectors commoncli.StringsFlag
// Match used when filtering by selectors
matchSelectorsOn string
// Filters agents to those that are banned.
banned commoncli.BoolFlag
// Filters agents by those that expire before this value.
expiresBefore string
// Filters agents to those matching the attestation type.
attestationType string
// Filters agents that can re-attest.
canReattest commoncli.BoolFlag
env *commoncli.Env
env *commoncli.Env
printer cliprinter.Printer
}
@ -62,61 +39,8 @@ func (*countCommand) Synopsis() string {
// Run counts attested agents
func (c *countCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient util.ServerClient) error {
filter := &agentv1.CountAgentsRequest_Filter{}
if len(c.selectors) > 0 {
matchBehavior, err := parseToSelectorMatch(c.matchSelectorsOn)
if err != nil {
return err
}
selectors := make([]*types.Selector, len(c.selectors))
for i, sel := range c.selectors {
selector, err := util.ParseSelector(sel)
if err != nil {
return fmt.Errorf("error parsing selector %q: %w", sel, err)
}
selectors[i] = selector
}
filter.BySelectorMatch = &types.SelectorMatch{
Selectors: selectors,
Match: matchBehavior,
}
}
if c.expiresBefore != "" {
// Parse the time string into a time.Time object
_, err := time.Parse("2006-01-02 15:04:05 -0700 -07", c.expiresBefore)
if err != nil {
return fmt.Errorf("date is not valid: %w", err)
}
filter.ByExpiresBefore = c.expiresBefore
}
if c.attestationType != "" {
filter.ByAttestationType = c.attestationType
}
// 0: all, 1: can't reattest, 2: can reattest
if c.canReattest == 1 {
filter.ByCanReattest = wrapperspb.Bool(false)
}
if c.canReattest == 2 {
filter.ByCanReattest = wrapperspb.Bool(true)
}
// 0: all, 1: no-banned, 2: banned
if c.banned == 1 {
filter.ByBanned = wrapperspb.Bool(false)
}
if c.banned == 2 {
filter.ByBanned = wrapperspb.Bool(true)
}
agentClient := serverClient.NewAgentClient()
countResponse, err := agentClient.CountAgents(ctx, &agentv1.CountAgentsRequest{
Filter: filter,
})
countResponse, err := agentClient.CountAgents(ctx, &agentv1.CountAgentsRequest{})
if err != nil {
return err
}
@ -125,16 +49,10 @@ func (c *countCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient u
}
func (c *countCommand) AppendFlags(fs *flag.FlagSet) {
fs.Var(&c.selectors, "selector", "A colon-delimited type:value selector. Can be used more than once")
fs.StringVar(&c.attestationType, "attestationType", "", "Filter by attestation type, like join_token or x509pop.")
fs.Var(&c.canReattest, "canReattest", "Filter based on string received, 'true': agents that can reattest, 'false': agents that can't reattest, other value will return all.")
fs.Var(&c.banned, "banned", "Filter based on string received, 'true': banned agents, 'false': not banned agents, other value will return all.")
fs.StringVar(&c.expiresBefore, "expiresBefore", "", "Filter by expiration time (format: \"2006-01-02 15:04:05 -0700 -07\")")
fs.StringVar(&c.matchSelectorsOn, "matchSelectorsOn", "superset", "The match mode used when filtering by selectors. Options: exact, any, superset and subset")
cliprinter.AppendFlagWithCustomPretty(&c.printer, fs, c.env, prettyPrintCount)
}
func prettyPrintCount(env *commoncli.Env, results ...any) error {
func prettyPrintCount(env *commoncli.Env, results ...interface{}) error {
countResp, ok := results[0].(*agentv1.CountAgentsResponse)
if !ok {
return errors.New("internal error: cli printer; please report this bug")

View File

@ -1,7 +1,6 @@
package agent
import (
"context"
"errors"
"flag"
@ -12,6 +11,7 @@ import (
commoncli "github.com/spiffe/spire/pkg/common/cli"
"github.com/spiffe/spire/pkg/common/cliprinter"
"github.com/spiffe/spire/pkg/server/api"
"golang.org/x/net/context"
)
type evictCommand struct {
@ -65,7 +65,7 @@ func (c *evictCommand) AppendFlags(fs *flag.FlagSet) {
cliprinter.AppendFlagWithCustomPretty(&c.printer, fs, c.env, prettyPrintEvictResult)
}
func prettyPrintEvictResult(env *commoncli.Env, _ ...any) error {
func prettyPrintEvictResult(env *commoncli.Env, _ ...interface{}) error {
env.Println("Agent evicted successfully")
return nil
}

View File

@ -1,7 +1,6 @@
package agent
import (
"context"
"errors"
"flag"
"fmt"
@ -14,32 +13,17 @@ import (
commoncli "github.com/spiffe/spire/pkg/common/cli"
"github.com/spiffe/spire/pkg/common/cliprinter"
"github.com/spiffe/spire/pkg/common/idutil"
"google.golang.org/protobuf/types/known/wrapperspb"
"golang.org/x/net/context"
)
type listCommand struct {
env *commoncli.Env
// Type and value are delimited by a colon (:)
// ex. "unix:uid:1000" or "spiffe_id:spiffe://example.org/foo"
selectors commoncli.StringsFlag
// Match used when filtering by selectors
// Match used when filtering agents by selectors
matchSelectorsOn string
// Filters agents to those that are banned.
banned commoncli.BoolFlag
// Filters agents by those that expire before this value.
expiresBefore string
// Filters agents to those matching the attestation type.
attestationType string
// Filters agents that can re-attest.
canReattest commoncli.BoolFlag
env *commoncli.Env
printer cliprinter.Printer
printer cliprinter.Printer
}
// NewListCommand creates a new "list" subcommand for "agent" command.
@ -84,35 +68,6 @@ func (c *listCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient ut
}
}
if c.expiresBefore != "" {
// Parse the time string into a time.Time object
_, err := time.Parse("2006-01-02 15:04:05 -0700 -07", c.expiresBefore)
if err != nil {
return fmt.Errorf("date is not valid: %w", err)
}
filter.ByExpiresBefore = c.expiresBefore
}
if c.attestationType != "" {
filter.ByAttestationType = c.attestationType
}
// 0: all, 1: can't reattest, 2: can reattest
if c.canReattest == 1 {
filter.ByCanReattest = wrapperspb.Bool(false)
}
if c.canReattest == 2 {
filter.ByCanReattest = wrapperspb.Bool(true)
}
// 0: all, 1: no-banned, 2: banned
if c.banned == 1 {
filter.ByBanned = wrapperspb.Bool(false)
}
if c.banned == 2 {
filter.ByBanned = wrapperspb.Bool(true)
}
agentClient := serverClient.NewAgentClient()
pageToken := ""
@ -136,16 +91,12 @@ func (c *listCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient ut
}
func (c *listCommand) AppendFlags(fs *flag.FlagSet) {
fs.Var(&c.selectors, "selector", "A colon-delimited type:value selector. Can be used more than once")
fs.StringVar(&c.attestationType, "attestationType", "", "Filter by attestation type, like join_token or x509pop.")
fs.Var(&c.canReattest, "canReattest", "Filter based on string received, 'true': agents that can reattest, 'false': agents that can't reattest, other value will return all.")
fs.Var(&c.banned, "banned", "Filter based on string received, 'true': banned agents, 'false': not banned agents, other value will return all.")
fs.StringVar(&c.expiresBefore, "expiresBefore", "", "Filter by expiration time (format: \"2006-01-02 15:04:05 -0700 -07\")")
fs.StringVar(&c.matchSelectorsOn, "matchSelectorsOn", "superset", "The match mode used when filtering by selectors. Options: exact, any, superset and subset")
fs.Var(&c.selectors, "selector", "A colon-delimited type:value selector. Can be used more than once")
cliprinter.AppendFlagWithCustomPretty(&c.printer, fs, c.env, prettyPrintAgents)
}
func prettyPrintAgents(env *commoncli.Env, results ...any) error {
func prettyPrintAgents(env *commoncli.Env, results ...interface{}) error {
listResp, ok := results[0].(*agentv1.ListAgentsResponse)
if !ok {
return errors.New("internal error: cli printer; please report this bug")

View File

@ -1,7 +1,6 @@
package agent
import (
"context"
"flag"
"fmt"
"time"
@ -14,6 +13,7 @@ import (
commoncli "github.com/spiffe/spire/pkg/common/cli"
"github.com/spiffe/spire/pkg/common/cliprinter"
"github.com/spiffe/spire/pkg/common/idutil"
"golang.org/x/net/context"
"google.golang.org/protobuf/types/known/wrapperspb"
)
@ -95,8 +95,8 @@ type expiredAgent struct {
Error string `json:"error,omitempty"`
}
func (c *purgeCommand) prettyPrintPurgeResult(env *commoncli.Env, results ...any) error {
if expAgents, ok := results[0].([]any)[0].(*expiredAgents); ok {
func (c *purgeCommand) prettyPrintPurgeResult(env *commoncli.Env, results ...interface{}) error {
if expAgents, ok := results[0].([]interface{})[0].(*expiredAgents); ok {
if len(expAgents.Agents) == 0 {
env.Println("No agents to purge.")
return nil

View File

@ -1,7 +1,6 @@
package agent
import (
"context"
"errors"
"flag"
@ -13,11 +12,12 @@ import (
commoncli "github.com/spiffe/spire/pkg/common/cli"
"github.com/spiffe/spire/pkg/common/cliprinter"
"github.com/spiffe/spire/pkg/server/api"
"golang.org/x/net/context"
)
type showCommand struct {
env *commoncli.Env
// SPIFFE ID of the agent being shown
// SPIFFE ID of the agent being showed
spiffeID string
printer cliprinter.Printer
}
@ -66,7 +66,7 @@ func (c *showCommand) AppendFlags(fs *flag.FlagSet) {
cliprinter.AppendFlagWithCustomPretty(&c.printer, fs, c.env, prettyPrintAgent)
}
func prettyPrintAgent(env *commoncli.Env, results ...any) error {
func prettyPrintAgent(env *commoncli.Env, results ...interface{}) error {
agent, ok := results[0].(*types.Agent)
if !ok {
return errors.New("internal error: cli printer; please report this bug")

View File

@ -1,31 +0,0 @@
package authoritycommon
import (
"time"
localauthorityv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/localauthority/v1"
commoncli "github.com/spiffe/spire/pkg/common/cli"
)
func PrettyPrintJWTAuthorityState(env *commoncli.Env, authorityState *localauthorityv1.AuthorityState) {
prettyPrintAuthorityState(env, authorityState, false)
}
func PrettyPrintX509AuthorityState(env *commoncli.Env, authorityState *localauthorityv1.AuthorityState) {
prettyPrintAuthorityState(env, authorityState, true)
}
func prettyPrintAuthorityState(env *commoncli.Env, authorityState *localauthorityv1.AuthorityState, includeUpstreamAuthority bool) {
env.Printf(" Authority ID: %s\n", authorityState.AuthorityId)
env.Printf(" Expires at: %s\n", time.Unix(authorityState.ExpiresAt, 0).UTC())
if !includeUpstreamAuthority {
return
}
if authorityState.UpstreamAuthoritySubjectKeyId != "" {
env.Printf(" Upstream authority Subject Key ID: %s\n", authorityState.UpstreamAuthoritySubjectKeyId)
return
}
env.Println(" Upstream authority ID: No upstream authority")
}

View File

@ -1,174 +0,0 @@
package authoritycommontest
import (
"bytes"
"context"
"testing"
"github.com/mitchellh/cli"
localauthorityv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/localauthority/v1"
commoncli "github.com/spiffe/spire/pkg/common/cli"
"github.com/spiffe/spire/test/clitest"
"github.com/spiffe/spire/test/spiretest"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
)
var AvailableFormats = []string{"pretty", "json"}
type localAuthorityTest struct {
Stdin *bytes.Buffer
Stdout *bytes.Buffer
Stderr *bytes.Buffer
Args []string
Server *fakeLocalAuthorityServer
Client cli.Command
}
func (s *localAuthorityTest) afterTest(t *testing.T) {
t.Logf("TEST:%s", t.Name())
t.Logf("STDOUT:\n%s", s.Stdout.String())
t.Logf("STDIN:\n%s", s.Stdin.String())
t.Logf("STDERR:\n%s", s.Stderr.String())
}
func SetupTest(t *testing.T, newClient func(*commoncli.Env) cli.Command) *localAuthorityTest {
server := &fakeLocalAuthorityServer{}
addr := spiretest.StartGRPCServer(t, func(s *grpc.Server) {
localauthorityv1.RegisterLocalAuthorityServer(s, server)
})
stdin := new(bytes.Buffer)
stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer)
client := newClient(&commoncli.Env{
Stdin: stdin,
Stdout: stdout,
Stderr: stderr,
})
test := &localAuthorityTest{
Stdin: stdin,
Stdout: stdout,
Stderr: stderr,
Args: []string{clitest.AddrArg, clitest.GetAddr(addr)},
Server: server,
Client: client,
}
t.Cleanup(func() {
test.afterTest(t)
})
return test
}
type fakeLocalAuthorityServer struct {
localauthorityv1.UnsafeLocalAuthorityServer
ActiveJWT,
PreparedJWT,
OldJWT,
ActiveX509,
PreparedX509,
OldX509,
TaintedX509,
RevokedX509,
TaintedJWT,
RevokedJWT *localauthorityv1.AuthorityState
TaintedUpstreamAuthoritySubjectKeyId,
RevokedUpstreamAuthoritySubjectKeyId string
Err error
}
func (s *fakeLocalAuthorityServer) GetJWTAuthorityState(context.Context, *localauthorityv1.GetJWTAuthorityStateRequest) (*localauthorityv1.GetJWTAuthorityStateResponse, error) {
return &localauthorityv1.GetJWTAuthorityStateResponse{
Active: s.ActiveJWT,
Prepared: s.PreparedJWT,
Old: s.OldJWT,
}, s.Err
}
func (s *fakeLocalAuthorityServer) PrepareJWTAuthority(context.Context, *localauthorityv1.PrepareJWTAuthorityRequest) (*localauthorityv1.PrepareJWTAuthorityResponse, error) {
return &localauthorityv1.PrepareJWTAuthorityResponse{
PreparedAuthority: s.PreparedJWT,
}, s.Err
}
func (s *fakeLocalAuthorityServer) ActivateJWTAuthority(context.Context, *localauthorityv1.ActivateJWTAuthorityRequest) (*localauthorityv1.ActivateJWTAuthorityResponse, error) {
return &localauthorityv1.ActivateJWTAuthorityResponse{
ActivatedAuthority: s.ActiveJWT,
}, s.Err
}
func (s *fakeLocalAuthorityServer) TaintJWTAuthority(context.Context, *localauthorityv1.TaintJWTAuthorityRequest) (*localauthorityv1.TaintJWTAuthorityResponse, error) {
return &localauthorityv1.TaintJWTAuthorityResponse{
TaintedAuthority: s.TaintedJWT,
}, s.Err
}
func (s *fakeLocalAuthorityServer) RevokeJWTAuthority(context.Context, *localauthorityv1.RevokeJWTAuthorityRequest) (*localauthorityv1.RevokeJWTAuthorityResponse, error) {
return &localauthorityv1.RevokeJWTAuthorityResponse{
RevokedAuthority: s.RevokedJWT,
}, s.Err
}
func (s *fakeLocalAuthorityServer) GetX509AuthorityState(context.Context, *localauthorityv1.GetX509AuthorityStateRequest) (*localauthorityv1.GetX509AuthorityStateResponse, error) {
return &localauthorityv1.GetX509AuthorityStateResponse{
Active: s.ActiveX509,
Prepared: s.PreparedX509,
Old: s.OldX509,
}, s.Err
}
func (s *fakeLocalAuthorityServer) PrepareX509Authority(context.Context, *localauthorityv1.PrepareX509AuthorityRequest) (*localauthorityv1.PrepareX509AuthorityResponse, error) {
return &localauthorityv1.PrepareX509AuthorityResponse{
PreparedAuthority: s.PreparedX509,
}, s.Err
}
func (s *fakeLocalAuthorityServer) ActivateX509Authority(context.Context, *localauthorityv1.ActivateX509AuthorityRequest) (*localauthorityv1.ActivateX509AuthorityResponse, error) {
return &localauthorityv1.ActivateX509AuthorityResponse{
ActivatedAuthority: s.ActiveX509,
}, s.Err
}
func (s *fakeLocalAuthorityServer) TaintX509Authority(context.Context, *localauthorityv1.TaintX509AuthorityRequest) (*localauthorityv1.TaintX509AuthorityResponse, error) {
return &localauthorityv1.TaintX509AuthorityResponse{
TaintedAuthority: s.TaintedX509,
}, s.Err
}
func (s *fakeLocalAuthorityServer) TaintX509UpstreamAuthority(context.Context, *localauthorityv1.TaintX509UpstreamAuthorityRequest) (*localauthorityv1.TaintX509UpstreamAuthorityResponse, error) {
return &localauthorityv1.TaintX509UpstreamAuthorityResponse{
UpstreamAuthoritySubjectKeyId: s.TaintedUpstreamAuthoritySubjectKeyId,
}, s.Err
}
func (s *fakeLocalAuthorityServer) RevokeX509Authority(context.Context, *localauthorityv1.RevokeX509AuthorityRequest) (*localauthorityv1.RevokeX509AuthorityResponse, error) {
return &localauthorityv1.RevokeX509AuthorityResponse{
RevokedAuthority: s.RevokedX509,
}, s.Err
}
func (s *fakeLocalAuthorityServer) RevokeX509UpstreamAuthority(context.Context, *localauthorityv1.RevokeX509UpstreamAuthorityRequest) (*localauthorityv1.RevokeX509UpstreamAuthorityResponse, error) {
return &localauthorityv1.RevokeX509UpstreamAuthorityResponse{
UpstreamAuthoritySubjectKeyId: s.RevokedUpstreamAuthoritySubjectKeyId,
}, s.Err
}
func RequireOutputBasedOnFormat(t *testing.T, format, stdoutString string, expectedStdoutPretty, expectedStdoutJSON string) {
switch format {
case "pretty":
require.Contains(t, stdoutString, expectedStdoutPretty)
case "json":
if expectedStdoutJSON != "" {
require.JSONEq(t, expectedStdoutJSON, stdoutString)
} else {
require.Empty(t, stdoutString)
}
}
}

View File

@ -1,4 +1,5 @@
//go:build !windows
// +build !windows
package bundle

View File

@ -113,7 +113,7 @@ func TestSetHelp(t *testing.T) {
func TestSetSynopsis(t *testing.T) {
test := setupTest(t, newSetCommand)
require.Equal(t, "Creates or updates federated bundle data", test.client.Synopsis())
require.Equal(t, "Creates or updates bundle data", test.client.Synopsis())
}
func TestSet(t *testing.T) {
@ -697,7 +697,7 @@ func TestDeleteHelp(t *testing.T) {
func TestDeleteSynopsis(t *testing.T) {
test := setupTest(t, newDeleteCommand)
require.Equal(t, "Deletes federated bundle data", test.client.Synopsis())
require.Equal(t, "Deletes bundle data", test.client.Synopsis())
}
func TestDelete(t *testing.T) {

View File

@ -1,4 +1,5 @@
//go:build windows
// +build windows
package bundle

View File

@ -2,6 +2,7 @@ package bundle
import (
"bytes"
"crypto"
"crypto/x509"
"encoding/json"
"encoding/pem"
@ -16,7 +17,7 @@ import (
"github.com/spiffe/go-spiffe/v2/spiffeid"
"github.com/spiffe/spire-api-sdk/proto/spire/api/types"
"github.com/spiffe/spire/cmd/spire-server/util"
"github.com/spiffe/spire/pkg/common/jwtutil"
"github.com/zeebo/errs"
)
const (
@ -77,7 +78,7 @@ func printBundle(out io.Writer, bundle *types.Bundle) error {
docBytes, err := b.Marshal()
if err != nil {
return err
return errs.Wrap(err)
}
var o bytes.Buffer
@ -85,8 +86,11 @@ func printBundle(out io.Writer, bundle *types.Bundle) error {
return err
}
_, err = fmt.Fprintln(out, o.String())
return err
if _, err := fmt.Fprintln(out, o.String()); err != nil {
return errs.Wrap(err)
}
return nil
}
// bundleFromProto converts a bundle from the given *types.Bundle to *spiffebundle.Bundle
@ -99,7 +103,7 @@ func bundleFromProto(bundleProto *types.Bundle) (*spiffebundle.Bundle, error) {
if err != nil {
return nil, err
}
jwtAuthorities, err := jwtutil.JWTKeysFromProto(bundleProto.JwtAuthorities)
jwtAuthorities, err := jwtKeysFromProto(bundleProto.JwtAuthorities)
if err != nil {
return nil, err
}
@ -128,6 +132,20 @@ func x509CertificatesFromProto(proto []*types.X509Certificate) ([]*x509.Certific
return certs, nil
}
// jwtKeysFromProto converts JWT keys from the given []*types.JWTKey to map[string]crypto.PublicKey.
// The key ID of the public key is used as the key in the returned map.
func jwtKeysFromProto(proto []*types.JWTKey) (map[string]crypto.PublicKey, error) {
keys := make(map[string]crypto.PublicKey)
for i, publicKey := range proto {
jwtSigningKey, err := x509.ParsePKIXPublicKey(publicKey.PublicKey)
if err != nil {
return nil, fmt.Errorf("unable to parse JWT signing key %d: %w", i, err)
}
keys[publicKey.KeyId] = jwtSigningKey
}
return keys, nil
}
func printBundleWithFormat(out io.Writer, bundle *types.Bundle, format string, header bool) error {
if bundle == nil {
return errors.New("no bundle provided")

View File

@ -9,9 +9,9 @@ import (
"github.com/mitchellh/cli"
bundlev1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/bundle/v1"
"github.com/spiffe/spire-api-sdk/proto/spire/api/types"
"github.com/spiffe/spire/cmd/spire-server/cli/common"
common_cli "github.com/spiffe/spire/pkg/common/cli"
"github.com/spiffe/spire/pkg/common/pemutil"
"github.com/spiffe/spire/test/clitest"
"github.com/spiffe/spire/test/spiretest"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
@ -203,7 +203,7 @@ func setupTest(t *testing.T, newClient func(*common_cli.Env) cli.Command) *bundl
cert1: cert1,
cert2: cert2,
key1Pkix: key1Pkix,
addr: clitest.GetAddr(addr),
addr: common.GetAddr(addr),
stdin: stdin,
stdout: stdout,
stderr: stderr,
@ -241,7 +241,7 @@ func (s *bundleTest) afterTest(t *testing.T) {
}
func (s *bundleTest) args(extra ...string) []string {
return append([]string{clitest.AddrArg, s.addr}, extra...)
return append([]string{common.AddrArg, s.addr}, extra...)
}
type fakeBundleServer struct {

View File

@ -1,7 +1,6 @@
package bundle
import (
"context"
"flag"
"fmt"
@ -11,6 +10,8 @@ import (
bundlev1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/bundle/v1"
"github.com/spiffe/spire/cmd/spire-server/util"
commoncli "github.com/spiffe/spire/pkg/common/cli"
"golang.org/x/net/context"
)
type countCommand struct {
@ -52,7 +53,7 @@ func (c *countCommand) AppendFlags(fs *flag.FlagSet) {
cliprinter.AppendFlagWithCustomPretty(&c.printer, fs, c.env, prettyPrintCount)
}
func prettyPrintCount(env *commoncli.Env, results ...any) error {
func prettyPrintCount(env *commoncli.Env, results ...interface{}) error {
countResp, ok := results[0].(*bundlev1.CountBundlesResponse)
if !ok {
return cliprinter.ErrInternalCustomPrettyFunc

View File

@ -44,7 +44,7 @@ func (c *deleteCommand) Name() string {
}
func (c *deleteCommand) Synopsis() string {
return "Deletes federated bundle data"
return "Deletes bundle data"
}
func (c *deleteCommand) AppendFlags(fs *flag.FlagSet) {
@ -77,7 +77,7 @@ func (c *deleteCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient
return c.printer.PrintProto(resp)
}
func prettyPrintDelete(env *commoncli.Env, results ...any) error {
func prettyPrintDelete(env *commoncli.Env, results ...interface{}) error {
deleteResp, ok := results[0].(*bundlev1.BatchDeleteFederatedBundleResponse)
if !ok {
return cliprinter.ErrInternalCustomPrettyFunc

View File

@ -63,7 +63,7 @@ func (c *listCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient ut
return c.printer.PrintProto(resp)
}
func (c *listCommand) prettyPrintList(env *commoncli.Env, results ...any) error {
func (c *listCommand) prettyPrintList(env *commoncli.Env, results ...interface{}) error {
if listResp, ok := results[0].(*bundlev1.ListFederatedBundlesResponse); ok {
for i, bundle := range listResp.Bundles {
if i != 0 {

View File

@ -39,7 +39,7 @@ func (c *setCommand) Name() string {
}
func (c *setCommand) Synopsis() string {
return "Creates or updates federated bundle data"
return "Creates or updates bundle data"
}
func (c *setCommand) AppendFlags(fs *flag.FlagSet) {
@ -80,7 +80,7 @@ func (c *setCommand) Run(ctx context.Context, env *common_cli.Env, serverClient
return c.printer.PrintProto(resp)
}
func prettyPrintSet(env *common_cli.Env, results ...any) error {
func prettyPrintSet(env *common_cli.Env, results ...interface{}) error {
setResp, ok := results[0].(*bundlev1.BatchSetFederatedBundleResponse)
if !ok {
return cliprinter.ErrInternalCustomPrettyFunc

View File

@ -51,7 +51,7 @@ func (c *showCommand) Run(ctx context.Context, _ *common_cli.Env, serverClient u
return c.printer.PrintProto(resp)
}
func (c *showCommand) prettyPrintBundle(env *common_cli.Env, results ...any) error {
func (c *showCommand) prettyPrintBundle(env *common_cli.Env, results ...interface{}) error {
showResp, ok := results[0].(*types.Bundle)
if !ok {
return cliprinter.ErrInternalCustomPrettyFunc

View File

@ -11,12 +11,8 @@ import (
"github.com/spiffe/spire/cmd/spire-server/cli/federation"
"github.com/spiffe/spire/cmd/spire-server/cli/healthcheck"
"github.com/spiffe/spire/cmd/spire-server/cli/jwt"
localauthority_jwt "github.com/spiffe/spire/cmd/spire-server/cli/localauthority/jwt"
localauthority_x509 "github.com/spiffe/spire/cmd/spire-server/cli/localauthority/x509"
"github.com/spiffe/spire/cmd/spire-server/cli/logger"
"github.com/spiffe/spire/cmd/spire-server/cli/run"
"github.com/spiffe/spire/cmd/spire-server/cli/token"
"github.com/spiffe/spire/cmd/spire-server/cli/upstreamauthority"
"github.com/spiffe/spire/cmd/spire-server/cli/validate"
"github.com/spiffe/spire/cmd/spire-server/cli/x509"
"github.com/spiffe/spire/pkg/common/log"
@ -100,15 +96,6 @@ func (cc *CLI) Run(ctx context.Context, args []string) int {
"federation update": func() (cli.Command, error) {
return federation.NewUpdateCommand(), nil
},
"logger get": func() (cli.Command, error) {
return logger.NewGetCommand(), nil
},
"logger set": func() (cli.Command, error) {
return logger.NewSetCommand(), nil
},
"logger reset": func() (cli.Command, error) {
return logger.NewResetCommand(), nil
},
"run": func() (cli.Command, error) {
return run.NewRunCommand(ctx, cc.LogOptions, cc.AllowUnknownConfig), nil
},
@ -127,42 +114,6 @@ func (cc *CLI) Run(ctx context.Context, args []string) int {
"validate": func() (cli.Command, error) {
return validate.NewValidateCommand(), nil
},
"localauthority x509 show": func() (cli.Command, error) {
return localauthority_x509.NewX509ShowCommand(), nil
},
"localauthority x509 prepare": func() (cli.Command, error) {
return localauthority_x509.NewX509PrepareCommand(), nil
},
"localauthority x509 activate": func() (cli.Command, error) {
return localauthority_x509.NewX509ActivateCommand(), nil
},
"localauthority x509 taint": func() (cli.Command, error) {
return localauthority_x509.NewX509TaintCommand(), nil
},
"localauthority x509 revoke": func() (cli.Command, error) {
return localauthority_x509.NewX509RevokeCommand(), nil
},
"localauthority jwt show": func() (cli.Command, error) {
return localauthority_jwt.NewJWTShowCommand(), nil
},
"localauthority jwt prepare": func() (cli.Command, error) {
return localauthority_jwt.NewJWTPrepareCommand(), nil
},
"localauthority jwt activate": func() (cli.Command, error) {
return localauthority_jwt.NewJWTActivateCommand(), nil
},
"localauthority jwt taint": func() (cli.Command, error) {
return localauthority_jwt.NewJWTTaintCommand(), nil
},
"localauthority jwt revoke": func() (cli.Command, error) {
return localauthority_jwt.NewJWTRevokeCommand(), nil
},
"upstreamauthority taint": func() (cli.Command, error) {
return upstreamauthority.NewTaintCommand(), nil
},
"upstreamauthority revoke": func() (cli.Command, error) {
return upstreamauthority.NewRevokeCommand(), nil
},
}
exitStatus, err := c.Run()

View File

@ -1,10 +1,13 @@
//go:build !windows
// +build !windows
package clitest
package common
import "net"
var (
AddrArg = "-socketPath"
AddrError = "rpc error: code = Unavailable desc = connection error: desc = \"transport: Error while dialing: dial unix ///does-not-exist.sock: connect: no such file or directory\"\n"
AddrError = "Error: connection error: desc = \"transport: error while dialing: dial unix /does-not-exist.sock: connect: no such file or directory\"\n"
AddrOutputUsage = `
-output value
Desired output format (pretty, json); default: pretty.
@ -13,3 +16,7 @@ var (
`
AddrValue = "/does-not-exist.sock"
)
func GetAddr(addr net.Addr) string {
return addr.String()
}

View File

@ -0,0 +1,26 @@
//go:build windows
// +build windows
package common
import (
"net"
"github.com/spiffe/spire/pkg/common/namedpipe"
)
var (
AddrArg = "-namedPipeName"
AddrError = "Error: connection error: desc = \"transport: error while dialing: open \\\\\\\\.\\\\pipe\\\\does-not-exist: The system cannot find the file specified.\"\n"
AddrOutputUsage = `
-namedPipeName string
Pipe name of the SPIRE Server API named pipe (default "\\spire-server\\private\\api")
-output value
Desired output format (pretty, json); default: pretty.
`
AddrValue = "\\does-not-exist"
)
func GetAddr(addr net.Addr) string {
return namedpipe.GetPipeName(addr.String())
}

View File

@ -1,45 +1,20 @@
package entry
import (
"context"
"flag"
"fmt"
"github.com/mitchellh/cli"
"github.com/spiffe/spire/pkg/common/cliprinter"
entryv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/entry/v1"
"github.com/spiffe/spire-api-sdk/proto/spire/api/types"
"github.com/spiffe/spire/cmd/spire-server/util"
commoncli "github.com/spiffe/spire/pkg/common/cli"
"github.com/spiffe/spire/pkg/common/cliprinter"
"google.golang.org/protobuf/types/known/wrapperspb"
"golang.org/x/net/context"
)
type countCommand struct {
// Type and value are delimited by a colon (:)
// ex. "unix:uid:1000" or "spiffe_id:spiffe://example.org/foo"
selectors StringsFlag
// Workload parent spiffeID
parentID string
// Workload spiffeID
spiffeID string
// Entry hint
hint string
// List of SPIFFE IDs of trust domains the registration entry is federated with
federatesWith StringsFlag
// Whether the entry is for a downstream SPIRE server
downstream bool
// Match used when filtering by federates with
matchFederatesWithOn string
// Match used when filtering by selectors
matchSelectorsOn string
printer cliprinter.Printer
env *commoncli.Env
}
@ -66,66 +41,7 @@ func (*countCommand) Synopsis() string {
// Run counts attested entries
func (c *countCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient util.ServerClient) error {
entryClient := serverClient.NewEntryClient()
filter := &entryv1.CountEntriesRequest_Filter{}
if c.parentID != "" {
id, err := idStringToProto(c.parentID)
if err != nil {
return fmt.Errorf("error parsing parent ID %q: %w", c.parentID, err)
}
filter.ByParentId = id
}
if c.spiffeID != "" {
id, err := idStringToProto(c.spiffeID)
if err != nil {
return fmt.Errorf("error parsing SPIFFE ID %q: %w", c.spiffeID, err)
}
filter.BySpiffeId = id
}
if len(c.selectors) != 0 {
matchSelectorBehavior, err := parseToSelectorMatch(c.matchSelectorsOn)
if err != nil {
return err
}
selectors := make([]*types.Selector, len(c.selectors))
for i, sel := range c.selectors {
selector, err := util.ParseSelector(sel)
if err != nil {
return fmt.Errorf("error parsing selectors: %w", err)
}
selectors[i] = selector
}
filter.BySelectors = &types.SelectorMatch{
Selectors: selectors,
Match: matchSelectorBehavior,
}
}
filter.ByDownstream = wrapperspb.Bool(c.downstream)
if len(c.federatesWith) > 0 {
matchFederatesWithBehavior, err := parseToFederatesWithMatch(c.matchFederatesWithOn)
if err != nil {
return err
}
filter.ByFederatesWith = &types.FederatesWithMatch{
TrustDomains: c.federatesWith,
Match: matchFederatesWithBehavior,
}
}
if c.hint != "" {
filter.ByHint = wrapperspb.String(c.hint)
}
countResponse, err := entryClient.CountEntries(ctx, &entryv1.CountEntriesRequest{
Filter: filter,
})
countResponse, err := entryClient.CountEntries(ctx, &entryv1.CountEntriesRequest{})
if err != nil {
return err
}
@ -134,19 +50,10 @@ func (c *countCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient u
}
func (c *countCommand) AppendFlags(fs *flag.FlagSet) {
fs.StringVar(&c.parentID, "parentID", "", "The Parent ID of the records to count")
fs.StringVar(&c.spiffeID, "spiffeID", "", "The SPIFFE ID of the records to count")
fs.BoolVar(&c.downstream, "downstream", false, "A boolean value that, when set, indicates that the entry describes a downstream SPIRE server")
fs.Var(&c.selectors, "selector", "A colon-delimited type:value selector. Can be used more than once")
fs.Var(&c.federatesWith, "federatesWith", "SPIFFE ID of a trust domain an entry is federate with. Can be used more than once")
fs.StringVar(&c.matchFederatesWithOn, "matchFederatesWithOn", "superset", "The match mode used when filtering by federates with. Options: exact, any, superset and subset")
fs.StringVar(&c.matchSelectorsOn, "matchSelectorsOn", "superset", "The match mode used when filtering by selectors. Options: exact, any, superset and subset")
fs.StringVar(&c.hint, "hint", "", "The Hint of the records to count (optional)")
cliprinter.AppendFlagWithCustomPretty(&c.printer, fs, c.env, c.prettyPrintCount)
}
func (c *countCommand) prettyPrintCount(env *commoncli.Env, results ...any) error {
func (c *countCommand) prettyPrintCount(env *commoncli.Env, results ...interface{}) error {
countResp, ok := results[0].(*entryv1.CountEntriesResponse)
if !ok {
return cliprinter.ErrInternalCustomPrettyFunc

View File

@ -5,11 +5,9 @@ import (
"testing"
entryv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/entry/v1"
"github.com/spiffe/spire-api-sdk/proto/spire/api/types"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/wrapperspb"
)
func TestCountHelp(t *testing.T) {
@ -33,262 +31,12 @@ func TestCount(t *testing.T) {
for _, tt := range []struct {
name string
args []string
expCountReq *entryv1.CountEntriesRequest
fakeCountResp *entryv1.CountEntriesResponse
serverErr error
expOutPretty string
expOutJSON string
expErr string
}{
{
name: "Count all entries (empty filter)",
expCountReq: &entryv1.CountEntriesRequest{
Filter: &entryv1.CountEntriesRequest_Filter{
ByDownstream: wrapperspb.Bool(false),
},
},
fakeCountResp: fakeResp4,
expOutPretty: "4 registration entries",
expOutJSON: `{"count":4}`,
},
{
name: "Count by parentID",
args: []string{"-parentID", "spiffe://example.org/father"},
expCountReq: &entryv1.CountEntriesRequest{
Filter: &entryv1.CountEntriesRequest_Filter{
ByParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/father"},
ByDownstream: wrapperspb.Bool(false),
},
},
fakeCountResp: fakeResp2,
expOutPretty: "2 registration entries",
expOutJSON: `{"count":2}`,
},
{
name: "Count by parent ID using invalid ID",
args: []string{"-parentID", "invalid-id"},
expErr: "Error: error parsing parent ID \"invalid-id\": scheme is missing or invalid\n",
},
{
name: "Count by SPIFFE ID",
args: []string{"-spiffeID", "spiffe://example.org/daughter"},
expCountReq: &entryv1.CountEntriesRequest{
Filter: &entryv1.CountEntriesRequest_Filter{
BySpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/daughter"},
ByDownstream: wrapperspb.Bool(false),
},
},
fakeCountResp: fakeResp2,
expOutPretty: "2 registration entries",
expOutJSON: `{"count":2}`,
},
{
name: "Count by SPIFFE ID using invalid ID",
args: []string{"-spiffeID", "invalid-id"},
expErr: "Error: error parsing SPIFFE ID \"invalid-id\": scheme is missing or invalid\n",
},
{
name: "Count by selectors: default matcher",
args: []string{"-selector", "foo:bar", "-selector", "bar:baz"},
expCountReq: &entryv1.CountEntriesRequest{
Filter: &entryv1.CountEntriesRequest_Filter{
BySelectors: &types.SelectorMatch{
Selectors: []*types.Selector{
{Type: "foo", Value: "bar"},
{Type: "bar", Value: "baz"},
},
Match: types.SelectorMatch_MATCH_SUPERSET,
},
ByDownstream: wrapperspb.Bool(false),
},
},
fakeCountResp: fakeResp1,
expOutPretty: "1 registration entry",
expOutJSON: `{"count":1}`,
},
{
name: "Count by selectors: exact matcher",
args: []string{"-selector", "foo:bar", "-selector", "bar:baz", "-matchSelectorsOn", "exact"},
expCountReq: &entryv1.CountEntriesRequest{
Filter: &entryv1.CountEntriesRequest_Filter{
BySelectors: &types.SelectorMatch{
Selectors: []*types.Selector{
{Type: "foo", Value: "bar"},
{Type: "bar", Value: "baz"},
},
Match: types.SelectorMatch_MATCH_EXACT,
},
ByDownstream: wrapperspb.Bool(false),
},
},
fakeCountResp: fakeResp1,
expOutPretty: "1 registration entry",
expOutJSON: `{"count":1}`,
},
{
name: "Count by selectors: superset matcher",
args: []string{"-selector", "foo:bar", "-selector", "bar:baz", "-matchSelectorsOn", "superset"},
expCountReq: &entryv1.CountEntriesRequest{
Filter: &entryv1.CountEntriesRequest_Filter{
BySelectors: &types.SelectorMatch{
Selectors: []*types.Selector{
{Type: "foo", Value: "bar"},
{Type: "bar", Value: "baz"},
},
Match: types.SelectorMatch_MATCH_SUPERSET,
},
ByDownstream: wrapperspb.Bool(false),
},
},
fakeCountResp: fakeResp1,
expOutPretty: "1 registration entry",
expOutJSON: `{"count":1}`,
},
{
name: "Count by selectors: subset matcher",
args: []string{"-selector", "foo:bar", "-selector", "bar:baz", "-matchSelectorsOn", "subset"},
expCountReq: &entryv1.CountEntriesRequest{
Filter: &entryv1.CountEntriesRequest_Filter{
BySelectors: &types.SelectorMatch{
Selectors: []*types.Selector{
{Type: "foo", Value: "bar"},
{Type: "bar", Value: "baz"},
},
Match: types.SelectorMatch_MATCH_SUBSET,
},
ByDownstream: wrapperspb.Bool(false),
},
},
fakeCountResp: fakeResp1,
expOutPretty: "1 registration entry",
expOutJSON: `{"count":1}`,
},
{
name: "Count by selectors: Any matcher",
args: []string{"-selector", "foo:bar", "-selector", "bar:baz", "-matchSelectorsOn", "any"},
expCountReq: &entryv1.CountEntriesRequest{
Filter: &entryv1.CountEntriesRequest_Filter{
BySelectors: &types.SelectorMatch{
Selectors: []*types.Selector{
{Type: "foo", Value: "bar"},
{Type: "bar", Value: "baz"},
},
Match: types.SelectorMatch_MATCH_ANY,
},
ByDownstream: wrapperspb.Bool(false),
},
},
fakeCountResp: fakeResp1,
expOutPretty: "1 registration entry",
expOutJSON: `{"count":1}`,
},
{
name: "Count by selectors: Invalid matcher",
args: []string{"-selector", "foo:bar", "-selector", "bar:baz", "-matchSelectorsOn", "NO-MATCHER"},
expErr: "Error: match behavior \"NO-MATCHER\" unknown\n",
},
{
name: "Count by selector using invalid selector",
args: []string{"-selector", "invalid-selector"},
expErr: "Error: error parsing selectors: selector \"invalid-selector\" must be formatted as type:value\n",
},
{
name: "Server error",
args: []string{"-spiffeID", "spiffe://example.org/daughter"},
expCountReq: &entryv1.CountEntriesRequest{
Filter: &entryv1.CountEntriesRequest_Filter{
BySpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/daughter"},
ByDownstream: wrapperspb.Bool(false),
},
},
serverErr: status.Error(codes.Internal, "internal server error"),
expErr: "Error: rpc error: code = Internal desc = internal server error\n",
},
{
name: "Count by Federates With: default matcher",
args: []string{"-federatesWith", "spiffe://domain.test"},
expCountReq: &entryv1.CountEntriesRequest{
Filter: &entryv1.CountEntriesRequest_Filter{
ByFederatesWith: &types.FederatesWithMatch{
TrustDomains: []string{"spiffe://domain.test"},
Match: types.FederatesWithMatch_MATCH_SUPERSET,
},
ByDownstream: wrapperspb.Bool(false),
},
},
fakeCountResp: fakeResp1,
expOutPretty: "1 registration entry",
expOutJSON: `{"count":1}`,
},
{
name: "Count by Federates With: exact matcher",
args: []string{"-federatesWith", "spiffe://domain.test", "-matchFederatesWithOn", "exact"},
expCountReq: &entryv1.CountEntriesRequest{
Filter: &entryv1.CountEntriesRequest_Filter{
ByFederatesWith: &types.FederatesWithMatch{
TrustDomains: []string{"spiffe://domain.test"},
Match: types.FederatesWithMatch_MATCH_EXACT,
},
ByDownstream: wrapperspb.Bool(false),
},
},
fakeCountResp: fakeResp1,
expOutPretty: "1 registration entry",
expOutJSON: `{"count":1}`,
},
{
name: "Count by Federates With: Any matcher",
args: []string{"-federatesWith", "spiffe://domain.test", "-matchFederatesWithOn", "any"},
expCountReq: &entryv1.CountEntriesRequest{
Filter: &entryv1.CountEntriesRequest_Filter{
ByFederatesWith: &types.FederatesWithMatch{
TrustDomains: []string{"spiffe://domain.test"},
Match: types.FederatesWithMatch_MATCH_ANY,
},
ByDownstream: wrapperspb.Bool(false),
},
},
fakeCountResp: fakeResp1,
expOutPretty: "1 registration entry",
expOutJSON: `{"count":1}`,
},
{
name: "Count by Federates With: superset matcher",
args: []string{"-federatesWith", "spiffe://domain.test", "-matchFederatesWithOn", "superset"},
expCountReq: &entryv1.CountEntriesRequest{
Filter: &entryv1.CountEntriesRequest_Filter{
ByFederatesWith: &types.FederatesWithMatch{
TrustDomains: []string{"spiffe://domain.test"},
Match: types.FederatesWithMatch_MATCH_SUPERSET,
},
ByDownstream: wrapperspb.Bool(false),
},
},
fakeCountResp: fakeResp1,
expOutPretty: "1 registration entry",
expOutJSON: `{"count":1}`,
},
{
name: "Count by Federates With: subset matcher",
args: []string{"-federatesWith", "spiffe://domain.test", "-matchFederatesWithOn", "subset"},
expCountReq: &entryv1.CountEntriesRequest{
Filter: &entryv1.CountEntriesRequest_Filter{
ByFederatesWith: &types.FederatesWithMatch{
TrustDomains: []string{"spiffe://domain.test"},
Match: types.FederatesWithMatch_MATCH_SUBSET,
},
ByDownstream: wrapperspb.Bool(false),
},
},
fakeCountResp: fakeResp1,
expOutPretty: "1 registration entry",
expOutJSON: `{"count":1}`,
},
{
name: "Count by Federates With: Invalid matcher",
args: []string{"-federatesWith", "spiffe://domain.test", "-matchFederatesWithOn", "NO-MATCHER"},
expErr: "Error: match behavior \"NO-MATCHER\" unknown\n",
},
{
name: "4 entries",
fakeCountResp: fakeResp4,
@ -325,16 +73,13 @@ func TestCount(t *testing.T) {
test.server.err = tt.serverErr
test.server.countEntriesResp = tt.fakeCountResp
args := tt.args
args = append(args, "-output", format)
rc := test.client.Run(test.args(args...))
rc := test.client.Run(test.args(tt.args...))
if tt.expErr != "" {
require.Equal(t, 1, rc)
require.Equal(t, tt.expErr, test.stderr.String())
return
}
requireOutputBasedOnFormat(t, format, test.stdout.String(), tt.expOutPretty, tt.expOutJSON)
requireOutputBasedOnFormat(t, test.stdout.String(), format, tt.expOutPretty, tt.expOutJSON)
require.Equal(t, 0, rc)
})
}

View File

@ -1,20 +1,19 @@
package entry
import (
"context"
"errors"
"flag"
"fmt"
"github.com/mitchellh/cli"
entryv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/entry/v1"
"github.com/spiffe/spire-api-sdk/proto/spire/api/types"
serverutil "github.com/spiffe/spire/cmd/spire-server/util"
"github.com/spiffe/spire/cmd/spire-server/util"
commoncli "github.com/spiffe/spire/pkg/common/cli"
"github.com/spiffe/spire/pkg/common/cliprinter"
"github.com/spiffe/spire/pkg/common/idutil"
"github.com/spiffe/spire/pkg/common/util"
"google.golang.org/grpc/codes"
"golang.org/x/net/context"
)
// NewCreateCommand creates a new "create" subcommand for "entry" command.
@ -23,7 +22,7 @@ func NewCreateCommand() cli.Command {
}
func newCreateCommand(env *commoncli.Env) cli.Command {
return serverutil.AdaptCommand(env, &createCommand{env: env})
return util.AdaptCommand(env, &createCommand{env: env})
}
type createCommand struct {
@ -47,6 +46,11 @@ type createCommand struct {
// Entry hint, used to disambiguate entries with the same SPIFFE ID
hint string
// TTL for x509 and JWT SVIDs issued to this workload, unless type specific TTLs are set.
// This field is deprecated in favor of the x509SVIDTTL and jwtSVIDTTL fields and will be
// removed in a future release.
ttl int
// TTL for x509 SVIDs issued to this workload
x509SVIDTTL int
@ -56,13 +60,13 @@ type createCommand struct {
// List of SPIFFE IDs of trust domains the registration entry is federated with
federatesWith StringsFlag
// whether the registration entry is for an "admin" workload
// Whether or not the registration entry is for an "admin" workload
admin bool
// whether the entry is for a downstream SPIRE server
// Whether or not the entry is for a downstream SPIRE server
downstream bool
// whether the entry represents a node or group of nodes
// Whether or not the entry represents a node or group of nodes
node bool
// Expiry of entry
@ -91,8 +95,9 @@ func (c *createCommand) AppendFlags(f *flag.FlagSet) {
f.StringVar(&c.entryID, "entryID", "", "A custom ID for this registration entry (optional). If not set, a new entry ID will be generated")
f.StringVar(&c.parentID, "parentID", "", "The SPIFFE ID of this record's parent")
f.StringVar(&c.spiffeID, "spiffeID", "", "The SPIFFE ID that this record represents")
f.IntVar(&c.x509SVIDTTL, "x509SVIDTTL", 0, "The lifetime, in seconds, for x509-SVIDs issued based on this registration entry.")
f.IntVar(&c.jwtSVIDTTL, "jwtSVIDTTL", 0, "The lifetime, in seconds, for JWT-SVIDs issued based on this registration entry.")
f.IntVar(&c.ttl, "ttl", 0, "The lifetime, in seconds, for SVIDs issued based on this registration entry. This flag is deprecated in favor of x509SVIDTTL and jwtSVIDTTL and will be removed in a future version")
f.IntVar(&c.x509SVIDTTL, "x509SVIDTTL", 0, "The lifetime, in seconds, for x509-SVIDs issued based on this registration entry. Overrides ttl flag")
f.IntVar(&c.jwtSVIDTTL, "jwtSVIDTTL", 0, "The lifetime, in seconds, for JWT-SVIDs issued based on this registration entry. Overrides ttl flag")
f.StringVar(&c.path, "data", "", "Path to a file containing registration JSON (optional). If set to '-', read the JSON from stdin.")
f.Var(&c.selectors, "selector", "A colon-delimited type:value selector. Can be used more than once")
f.Var(&c.federatesWith, "federatesWith", "SPIFFE ID of a trust domain to federate with. Can be used more than once")
@ -106,7 +111,7 @@ func (c *createCommand) AppendFlags(f *flag.FlagSet) {
cliprinter.AppendFlagWithCustomPretty(&c.printer, f, c.env, prettyPrintCreate)
}
func (c *createCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient serverutil.ServerClient) error {
func (c *createCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient util.ServerClient) error {
if err := c.validate(); err != nil {
return err
}
@ -154,6 +159,10 @@ func (c *createCommand) validate() (err error) {
return errors.New("a SPIFFE ID is required")
}
if c.ttl < 0 {
return errors.New("a positive TTL is required")
}
if c.x509SVIDTTL < 0 {
return errors.New("a positive x509-SVID TTL is required")
}
@ -162,6 +171,10 @@ func (c *createCommand) validate() (err error) {
return errors.New("a positive JWT-SVID TTL is required")
}
if c.ttl > 0 && (c.x509SVIDTTL > 0 || c.jwtSVIDTTL > 0) {
return errors.New("use x509SVIDTTL and jwtSVIDTTL flags or the deprecated ttl flag")
}
return nil
}
@ -177,16 +190,6 @@ func (c *createCommand) parseConfig() ([]*types.Entry, error) {
return nil, err
}
x509SvidTTL, err := util.CheckedCast[int32](c.x509SVIDTTL)
if err != nil {
return nil, fmt.Errorf("invalid value for X509 SVID TTL: %w", err)
}
jwtSvidTTL, err := util.CheckedCast[int32](c.jwtSVIDTTL)
if err != nil {
return nil, fmt.Errorf("invalid value for JWT SVID TTL: %w", err)
}
e := &types.Entry{
Id: c.entryID,
ParentId: parentID,
@ -195,14 +198,26 @@ func (c *createCommand) parseConfig() ([]*types.Entry, error) {
ExpiresAt: c.entryExpiry,
DnsNames: c.dnsNames,
StoreSvid: c.storeSVID,
X509SvidTtl: x509SvidTTL,
JwtSvidTtl: jwtSvidTTL,
X509SvidTtl: int32(c.x509SVIDTTL),
JwtSvidTtl: int32(c.jwtSVIDTTL),
Hint: c.hint,
}
// c.ttl is deprecated but usable if the new c.x509Svid field is not used.
// c.ttl should not be used to set the jwtSVIDTTL value because the previous
// behavior was to have a hard-coded 5 minute JWT TTL no matter what the value
// of ttl was set to.
// validate(...) ensures that either the new fields or the deprecated field is
// used, but never a mixture.
//
// https://github.com/spiffe/spire/issues/2700
if e.X509SvidTtl == 0 {
e.X509SvidTtl = int32(c.ttl)
}
selectors := []*types.Selector{}
for _, s := range c.selectors {
cs, err := serverutil.ParseSelector(s)
cs, err := util.ParseSelector(s)
if err != nil {
return nil, err
}
@ -244,7 +259,7 @@ func getParentID(config *createCommand, td string) (*types.SPIFFEID, error) {
return idStringToProto(config.parentID)
}
func prettyPrintCreate(env *commoncli.Env, results ...any) error {
func prettyPrintCreate(env *commoncli.Env, results ...interface{}) error {
var succeeded, failed []*entryv1.BatchCreateEntryResponse_Result
createResp, ok := results[0].(*entryv1.BatchCreateEntryResponse)
if !ok {
@ -266,7 +281,7 @@ func prettyPrintCreate(env *commoncli.Env, results ...any) error {
for _, r := range failed {
env.ErrPrintf("Failed to create the following entry (code: %s, msg: %q):\n",
util.MustCast[codes.Code](r.Status.Code),
codes.Code(r.Status.Code),
r.Status.Message)
printEntry(r.Entry, env.ErrPrintf)
}

View File

@ -54,7 +54,7 @@ func TestCreate(t *testing.T) {
},
}
fakeRespOKFromCmdWithoutJwtTtl := &entryv1.BatchCreateEntryResponse{
fakeRespOKFromCmd2 := &entryv1.BatchCreateEntryResponse{
Results: []*entryv1.BatchCreateEntryResponse_Result{
{
Entry: &types.Entry{
@ -186,16 +186,28 @@ func TestCreate(t *testing.T) {
expErrJSON: "Error: selector \"unix\" must be formatted as type:value\n",
},
{
name: "Negative X509SvidTtl",
args: []string{"-selector", "unix", "-parentID", "spiffe://example.org/parent", "-spiffeID", "spiffe://example.org/workload", "-x509SVIDTTL", "-10"},
expErrPretty: "Error: a positive x509-SVID TTL is required\n",
expErrJSON: "Error: a positive x509-SVID TTL is required\n",
name: "Negative TTL",
args: []string{"-selector", "unix", "-parentID", "spiffe://example.org/parent", "-spiffeID", "spiffe://example.org/workload", "-ttl", "-10"},
expErrPretty: "Error: a positive TTL is required\n",
expErrJSON: "Error: a positive TTL is required\n",
},
{
name: "Negative jwtSVIDTTL",
args: []string{"-selector", "unix", "-parentID", "spiffe://example.org/parent", "-spiffeID", "spiffe://example.org/workload", "-jwtSVIDTTL", "-10"},
expErrPretty: "Error: a positive JWT-SVID TTL is required\n",
expErrJSON: "Error: a positive JWT-SVID TTL is required\n",
name: "Invalid TTL and X509SvidTtl",
args: []string{"-selector", "unix", "-parentID", "spiffe://example.org/parent", "-spiffeID", "spiffe://example.org/workload", "-ttl", "10", "-x509SVIDTTL", "20"},
expErrPretty: "Error: use x509SVIDTTL and jwtSVIDTTL flags or the deprecated ttl flag\n",
expErrJSON: "Error: use x509SVIDTTL and jwtSVIDTTL flags or the deprecated ttl flag\n",
},
{
name: "Invalid TTL and JwtSvidTtl",
args: []string{"-selector", "unix", "-parentID", "spiffe://example.org/parent", "-spiffeID", "spiffe://example.org/workload", "-ttl", "10", "-jwtSVIDTTL", "20"},
expErrPretty: "Error: use x509SVIDTTL and jwtSVIDTTL flags or the deprecated ttl flag\n",
expErrJSON: "Error: use x509SVIDTTL and jwtSVIDTTL flags or the deprecated ttl flag\n",
},
{
name: "Invalid TTL and both X509SvidTtl and JwtSvidTtl",
args: []string{"-selector", "unix", "-parentID", "spiffe://example.org/parent", "-spiffeID", "spiffe://example.org/workload", "-ttl", "10", "-x509SVIDTTL", "20", "-jwtSVIDTTL", "30"},
expErrPretty: "Error: use x509SVIDTTL and jwtSVIDTTL flags or the deprecated ttl flag\n",
expErrJSON: "Error: use x509SVIDTTL and jwtSVIDTTL flags or the deprecated ttl flag\n",
},
{
name: "Federated node entries",
@ -334,7 +346,7 @@ StoreSvid : true
"-parentID", "spiffe://example.org/parent",
"-selector", "zebra:zebra:2000",
"-selector", "alpha:alpha:2000",
"-x509SVIDTTL", "60",
"-ttl", "60",
"-federatesWith", "spiffe://domaina.test",
"-federatesWith", "spiffe://domainb.test",
"-admin",
@ -364,7 +376,111 @@ StoreSvid : true
},
},
},
fakeResp: fakeRespOKFromCmdWithoutJwtTtl,
fakeResp: fakeRespOKFromCmd2,
expOutPretty: fmt.Sprintf(`Entry ID : entry-id
SPIFFE ID : spiffe://example.org/workload
Parent ID : spiffe://example.org/parent
Revision : 0
Downstream : true
X509-SVID TTL : 60
JWT-SVID TTL : default
Expiration time : %s
Selector : zebra:zebra:2000
Selector : alpha:alpha:2000
FederatesWith : spiffe://domaina.test
FederatesWith : spiffe://domainb.test
DNS name : unu1000
DNS name : ung1000
Admin : true
StoreSvid : true
`, time.Unix(1552410266, 0).UTC()),
expOutJSON: `{
"results": [
{
"status": {
"code": 0,
"message": "OK"
},
"entry": {
"id": "entry-id",
"spiffe_id": {
"trust_domain": "example.org",
"path": "/workload"
},
"parent_id": {
"trust_domain": "example.org",
"path": "/parent"
},
"selectors": [
{
"type": "zebra",
"value": "zebra:2000"
},
{
"type": "alpha",
"value": "alpha:2000"
}
],
"x509_svid_ttl": 60,
"federates_with": [
"spiffe://domaina.test",
"spiffe://domainb.test"
],
"hint": "",
"admin": true,
"created_at": "1547583197",
"downstream": true,
"expires_at": "1552410266",
"dns_names": [
"unu1000",
"ung1000"
],
"revision_number": "0",
"store_svid": true,
"jwt_svid_ttl": 0
}
}
]
}`,
},
{
name: "Create succeeds using deprecated command line arguments",
args: []string{
"-spiffeID", "spiffe://example.org/workload",
"-parentID", "spiffe://example.org/parent",
"-selector", "zebra:zebra:2000",
"-selector", "alpha:alpha:2000",
"-ttl", "60",
"-federatesWith", "spiffe://domaina.test",
"-federatesWith", "spiffe://domainb.test",
"-admin",
"-entryExpiry", "1552410266",
"-dns", "unu1000",
"-dns", "ung1000",
"-downstream",
"-storeSVID",
},
expReq: &entryv1.BatchCreateEntryRequest{
Entries: []*types.Entry{
{
SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/workload"},
ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/parent"},
Selectors: []*types.Selector{
{Type: "zebra", Value: "zebra:2000"},
{Type: "alpha", Value: "alpha:2000"},
},
X509SvidTtl: 60,
FederatesWith: []string{"spiffe://domaina.test", "spiffe://domainb.test"},
Admin: true,
ExpiresAt: 1552410266,
DnsNames: []string{"unu1000", "ung1000"},
Downstream: true,
StoreSvid: true,
},
},
},
fakeResp: fakeRespOKFromCmd2,
expOutPretty: fmt.Sprintf(`Entry ID : entry-id
SPIFFE ID : spiffe://example.org/workload
Parent ID : spiffe://example.org/parent

View File

@ -1,7 +1,6 @@
package entry
import (
"context"
"encoding/json"
"errors"
"flag"
@ -10,11 +9,12 @@ import (
"github.com/mitchellh/cli"
entryv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/entry/v1"
serverutil "github.com/spiffe/spire/cmd/spire-server/util"
"github.com/spiffe/spire/cmd/spire-server/util"
commoncli "github.com/spiffe/spire/pkg/common/cli"
"github.com/spiffe/spire/pkg/common/cliprinter"
"github.com/spiffe/spire/pkg/common/util"
"google.golang.org/grpc/codes"
"golang.org/x/net/context"
)
// NewDeleteCommand creates a new "delete" subcommand for "entry" command.
@ -23,7 +23,7 @@ func NewDeleteCommand() cli.Command {
}
func newDeleteCommand(env *commoncli.Env) cli.Command {
return serverutil.AdaptCommand(env, &deleteCommand{env: env})
return util.AdaptCommand(env, &deleteCommand{env: env})
}
type deleteCommand struct {
@ -71,7 +71,7 @@ func parseEntryDeleteJSON(path string) ([]string, error) {
return batchDeleteEntryRequest.Ids, nil
}
func (c *deleteCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient serverutil.ServerClient) error {
func (c *deleteCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient util.ServerClient) error {
if err := c.validate(); err != nil {
return err
}
@ -109,7 +109,7 @@ func (c *deleteCommand) validate() error {
return nil
}
func (c *deleteCommand) prettyPrintDelete(env *commoncli.Env, results ...any) error {
func (c *deleteCommand) prettyPrintDelete(env *commoncli.Env, results ...interface{}) error {
deleteResp, ok := results[0].(*entryv1.BatchDeleteEntryResponse)
if !ok {
return cliprinter.ErrInternalCustomPrettyFunc
@ -136,7 +136,7 @@ func (c *deleteCommand) prettyPrintDelete(env *commoncli.Env, results ...any) er
for _, result := range failed {
env.ErrPrintf("Failed to delete entry with ID %s (code: %s, msg: %q)\n",
result.Id,
util.MustCast[codes.Code](result.Status.Code),
codes.Code(result.Status.Code),
result.Status.Message)
}

View File

@ -1,7 +1,6 @@
package entry
import (
"context"
"errors"
"flag"
"fmt"
@ -14,6 +13,8 @@ import (
"github.com/spiffe/spire/pkg/common/cliprinter"
commonutil "github.com/spiffe/spire/pkg/common/util"
"google.golang.org/protobuf/types/known/wrapperspb"
"golang.org/x/net/context"
)
const listEntriesRequestPageSize = 500
@ -47,7 +48,7 @@ type showCommand struct {
// List of SPIFFE IDs of trust domains the registration entry is federated with
federatesWith StringsFlag
// whether the entry is for a downstream SPIRE server
// Whether or not the entry is for a downstream SPIRE server
downstream bool
// Match used when filtering by federates with
@ -175,8 +176,6 @@ func (c *showCommand) fetchEntries(ctx context.Context, client entryv1.EntryClie
filter.ByHint = wrapperspb.String(c.hint)
}
filter.ByDownstream = wrapperspb.Bool(c.downstream)
pageToken := ""
for {
@ -247,7 +246,7 @@ func parseToFederatesWithMatch(match string) (types.FederatesWithMatch_MatchBeha
}
}
func prettyPrintShow(env *commoncli.Env, results ...any) error {
func prettyPrintShow(env *commoncli.Env, results ...interface{}) error {
listResp, ok := results[0].(*entryv1.ListEntriesResponse)
if !ok {
return cliprinter.ErrInternalCustomPrettyFunc

View File

@ -10,7 +10,6 @@ import (
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/wrapperspb"
)
func TestShowHelp(t *testing.T) {
@ -62,9 +61,7 @@ func TestShow(t *testing.T) {
name: "List all entries (empty filter)",
expListReq: &entryv1.ListEntriesRequest{
PageSize: listEntriesRequestPageSize,
Filter: &entryv1.ListEntriesRequest_Filter{
ByDownstream: wrapperspb.Bool(false),
},
Filter: &entryv1.ListEntriesRequest_Filter{},
},
fakeListResp: fakeRespAll,
expOutPretty: fmt.Sprintf("Found 4 entries\n%s%s%s%s",
@ -106,8 +103,7 @@ func TestShow(t *testing.T) {
expListReq: &entryv1.ListEntriesRequest{
PageSize: listEntriesRequestPageSize,
Filter: &entryv1.ListEntriesRequest_Filter{
ByParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/father"},
ByDownstream: wrapperspb.Bool(false),
ByParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/father"},
},
},
fakeListResp: fakeRespFather,
@ -128,8 +124,7 @@ func TestShow(t *testing.T) {
expListReq: &entryv1.ListEntriesRequest{
PageSize: listEntriesRequestPageSize,
Filter: &entryv1.ListEntriesRequest_Filter{
BySpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/daughter"},
ByDownstream: wrapperspb.Bool(false),
BySpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/daughter"},
},
},
fakeListResp: fakeRespDaughter,
@ -157,7 +152,6 @@ func TestShow(t *testing.T) {
},
Match: types.SelectorMatch_MATCH_SUPERSET,
},
ByDownstream: wrapperspb.Bool(false),
},
},
fakeListResp: fakeRespFatherDaughter,
@ -179,7 +173,6 @@ func TestShow(t *testing.T) {
},
Match: types.SelectorMatch_MATCH_EXACT,
},
ByDownstream: wrapperspb.Bool(false),
},
},
fakeListResp: fakeRespFatherDaughter,
@ -201,7 +194,6 @@ func TestShow(t *testing.T) {
},
Match: types.SelectorMatch_MATCH_SUPERSET,
},
ByDownstream: wrapperspb.Bool(false),
},
},
fakeListResp: fakeRespFatherDaughter,
@ -223,7 +215,6 @@ func TestShow(t *testing.T) {
},
Match: types.SelectorMatch_MATCH_SUBSET,
},
ByDownstream: wrapperspb.Bool(false),
},
},
fakeListResp: fakeRespFatherDaughter,
@ -245,7 +236,6 @@ func TestShow(t *testing.T) {
},
Match: types.SelectorMatch_MATCH_ANY,
},
ByDownstream: wrapperspb.Bool(false),
},
},
fakeListResp: fakeRespFatherDaughter,
@ -270,8 +260,7 @@ func TestShow(t *testing.T) {
expListReq: &entryv1.ListEntriesRequest{
PageSize: listEntriesRequestPageSize,
Filter: &entryv1.ListEntriesRequest_Filter{
BySpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/daughter"},
ByDownstream: wrapperspb.Bool(false),
BySpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/daughter"},
},
},
serverErr: status.Error(codes.Internal, "internal server error"),
@ -287,7 +276,6 @@ func TestShow(t *testing.T) {
TrustDomains: []string{"spiffe://domain.test"},
Match: types.FederatesWithMatch_MATCH_SUPERSET,
},
ByDownstream: wrapperspb.Bool(false),
},
},
fakeListResp: fakeRespMotherDaughter,
@ -306,7 +294,6 @@ func TestShow(t *testing.T) {
TrustDomains: []string{"spiffe://domain.test"},
Match: types.FederatesWithMatch_MATCH_EXACT,
},
ByDownstream: wrapperspb.Bool(false),
},
},
fakeListResp: fakeRespMotherDaughter,
@ -325,7 +312,6 @@ func TestShow(t *testing.T) {
TrustDomains: []string{"spiffe://domain.test"},
Match: types.FederatesWithMatch_MATCH_ANY,
},
ByDownstream: wrapperspb.Bool(false),
},
},
fakeListResp: fakeRespMotherDaughter,
@ -344,7 +330,6 @@ func TestShow(t *testing.T) {
TrustDomains: []string{"spiffe://domain.test"},
Match: types.FederatesWithMatch_MATCH_SUPERSET,
},
ByDownstream: wrapperspb.Bool(false),
},
},
fakeListResp: fakeRespMotherDaughter,
@ -363,7 +348,6 @@ func TestShow(t *testing.T) {
TrustDomains: []string{"spiffe://domain.test"},
Match: types.FederatesWithMatch_MATCH_SUBSET,
},
ByDownstream: wrapperspb.Bool(false),
},
},
fakeListResp: fakeRespMotherDaughter,
@ -445,7 +429,7 @@ func getEntries(count int) []*types.Entry {
}
e := []*types.Entry{}
for i := range count {
for i := 0; i < count; i++ {
e = append(e, entries[i])
}

View File

@ -1,19 +1,18 @@
package entry
import (
"context"
"errors"
"flag"
"fmt"
"github.com/mitchellh/cli"
entryv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/entry/v1"
"github.com/spiffe/spire-api-sdk/proto/spire/api/types"
serverutil "github.com/spiffe/spire/cmd/spire-server/util"
"github.com/spiffe/spire/cmd/spire-server/util"
commoncli "github.com/spiffe/spire/pkg/common/cli"
"github.com/spiffe/spire/pkg/common/cliprinter"
"github.com/spiffe/spire/pkg/common/util"
"google.golang.org/grpc/codes"
"golang.org/x/net/context"
)
// NewUpdateCommand creates a new "update" subcommand for "entry" command.
@ -22,7 +21,7 @@ func NewUpdateCommand() cli.Command {
}
func newUpdateCommand(env *commoncli.Env) cli.Command {
return serverutil.AdaptCommand(env, &updateCommand{env: env})
return util.AdaptCommand(env, &updateCommand{env: env})
}
type updateCommand struct {
@ -43,9 +42,12 @@ type updateCommand struct {
// Workload spiffeID
spiffeID string
// whether the entry is for a downstream SPIRE server
// Whether or not the entry is for a downstream SPIRE server
downstream bool
// TTL for certificates issued to this workload
ttl int
// TTL for x509 SVIDs issued to this workload
x509SvidTTL int
@ -55,7 +57,7 @@ type updateCommand struct {
// List of SPIFFE IDs of trust domains the registration entry is federated with
federatesWith StringsFlag
// whether the registration entry is for an "admin" workload
// Whether or not the registration entry is for an "admin" workload
admin bool
// Expiry of entry
@ -87,8 +89,9 @@ func (c *updateCommand) AppendFlags(f *flag.FlagSet) {
f.StringVar(&c.entryID, "entryID", "", "The Registration Entry ID of the record to update")
f.StringVar(&c.parentID, "parentID", "", "The SPIFFE ID of this record's parent")
f.StringVar(&c.spiffeID, "spiffeID", "", "The SPIFFE ID that this record represents")
f.IntVar(&c.x509SvidTTL, "x509SVIDTTL", 0, "The lifetime, in seconds, for x509-SVIDs issued based on this registration entry.")
f.IntVar(&c.jwtSvidTTL, "jwtSVIDTTL", 0, "The lifetime, in seconds, for JWT-SVIDs issued based on this registration entry.")
f.IntVar(&c.ttl, "ttl", 0, "The lifetime, in seconds, for SVIDs issued based on this registration entry. This flag is deprecated in favor of x509SVIDTTL and jwtSVIDTTL and will be removed in a future version")
f.IntVar(&c.x509SvidTTL, "x509SVIDTTL", 0, "The lifetime, in seconds, for x509-SVIDs issued based on this registration entry. Overrides ttl flag")
f.IntVar(&c.jwtSvidTTL, "jwtSVIDTTL", 0, "The lifetime, in seconds, for JWT-SVIDs issued based on this registration entry. Overrides ttl flag")
f.StringVar(&c.path, "data", "", "Path to a file containing registration JSON (optional). If set to '-', read the JSON from stdin.")
f.Var(&c.selectors, "selector", "A colon-delimited type:value selector. Can be used more than once")
f.Var(&c.federatesWith, "federatesWith", "SPIFFE ID of a trust domain to federate with. Can be used more than once")
@ -101,7 +104,7 @@ func (c *updateCommand) AppendFlags(f *flag.FlagSet) {
cliprinter.AppendFlagWithCustomPretty(&c.printer, f, c.env, prettyPrintUpdate)
}
func (c *updateCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient serverutil.ServerClient) error {
func (c *updateCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient util.ServerClient) error {
if err := c.validate(); err != nil {
return err
}
@ -149,6 +152,10 @@ func (c *updateCommand) validate() (err error) {
return errors.New("a SPIFFE ID is required")
}
if c.ttl < 0 {
return errors.New("a positive TTL is required")
}
if c.x509SvidTTL < 0 {
return errors.New("a positive x509-SVID TTL is required")
}
@ -157,6 +164,10 @@ func (c *updateCommand) validate() (err error) {
return errors.New("a positive JWT-SVID TTL is required")
}
if c.ttl > 0 && (c.x509SvidTTL > 0 || c.jwtSvidTTL > 0) {
return errors.New("use x509SVIDTTL and jwtSVIDTTL flags or the deprecated ttl flag")
}
return nil
}
@ -171,16 +182,6 @@ func (c *updateCommand) parseConfig() ([]*types.Entry, error) {
return nil, err
}
x509SvidTTL, err := util.CheckedCast[int32](c.x509SvidTTL)
if err != nil {
return nil, fmt.Errorf("invalid value for X509 SVID TTL: %w", err)
}
jwtSvidTTL, err := util.CheckedCast[int32](c.jwtSvidTTL)
if err != nil {
return nil, fmt.Errorf("invalid value for JWT SVID TTL: %w", err)
}
e := &types.Entry{
Id: c.entryID,
ParentId: parentID,
@ -188,14 +189,26 @@ func (c *updateCommand) parseConfig() ([]*types.Entry, error) {
Downstream: c.downstream,
ExpiresAt: c.entryExpiry,
DnsNames: c.dnsNames,
X509SvidTtl: x509SvidTTL,
JwtSvidTtl: jwtSvidTTL,
X509SvidTtl: int32(c.x509SvidTTL),
JwtSvidTtl: int32(c.jwtSvidTTL),
Hint: c.hint,
}
// c.ttl is deprecated but usable if the new c.x509Svid field is not used.
// c.ttl should not be used to set the jwtSVIDTTL value because the previous
// behavior was to have a hard-coded 5 minute JWT TTL no matter what the value
// of ttl was set to.
// validate(...) ensures that either the new fields or the deprecated field is
// used, but never a mixture.
//
// https://github.com/spiffe/spire/issues/2700
if e.X509SvidTtl == 0 {
e.X509SvidTtl = int32(c.ttl)
}
selectors := []*types.Selector{}
for _, s := range c.selectors {
cs, err := serverutil.ParseSelector(s)
cs, err := util.ParseSelector(s)
if err != nil {
return nil, err
}
@ -229,7 +242,7 @@ func updateEntries(ctx context.Context, c entryv1.EntryClient, entries []*types.
return
}
func prettyPrintUpdate(env *commoncli.Env, results ...any) error {
func prettyPrintUpdate(env *commoncli.Env, results ...interface{}) error {
var succeeded, failed []*entryv1.BatchUpdateEntryResponse_Result
updateResp, ok := results[0].(*entryv1.BatchUpdateEntryResponse)
if !ok {
@ -252,7 +265,7 @@ func prettyPrintUpdate(env *commoncli.Env, results ...any) error {
// Print entries that failed to be updated
for _, r := range failed {
env.ErrPrintf("Failed to update the following entry (code: %s, msg: %q):\n",
util.MustCast[codes.Code](r.Status.Code),
codes.Code(r.Status.Code),
r.Status.Message)
printEntry(r.Entry, env.ErrPrintf)
}

View File

@ -321,6 +321,24 @@ func TestUpdate(t *testing.T) {
JwtSvidTtl: 300,
}
entry5 := &types.Entry{
Id: "entry-id",
SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/workload"},
ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/parent"},
Selectors: []*types.Selector{
{Type: "zebra", Value: "zebra:2000"},
{Type: "alpha", Value: "alpha:2000"},
},
X509SvidTtl: 60,
JwtSvidTtl: 0,
FederatesWith: []string{"spiffe://domaina.test", "spiffe://domainb.test"},
Admin: true,
ExpiresAt: 1552410266,
DnsNames: []string{"unu1000", "ung1000"},
Downstream: true,
Hint: "external",
}
entry2Resp := proto.Clone(entry2).(*types.Entry)
entry2Resp.CreatedAt = 1547583197
entry3Resp := proto.Clone(entry3).(*types.Entry)
@ -398,6 +416,30 @@ func TestUpdate(t *testing.T) {
expErrPretty: "Error: selector \"unix\" must be formatted as type:value\n",
expErrJSON: "Error: selector \"unix\" must be formatted as type:value\n",
},
{
name: "Negative TTL",
args: []string{"-entryID", "entry-id", "-selector", "unix", "-parentID", "spiffe://example.org/parent", "-spiffeID", "spiffe://example.org/workload", "-ttl", "-10"},
expErrPretty: "Error: a positive TTL is required\n",
expErrJSON: "Error: a positive TTL is required\n",
},
{
name: "Invalid TTL and X509SvidTtl",
args: []string{"-entryID", "entry-id", "-selector", "unix", "-parentID", "spiffe://example.org/parent", "-spiffeID", "spiffe://example.org/workload", "-ttl", "10", "-x509SVIDTTL", "20"},
expErrPretty: "Error: use x509SVIDTTL and jwtSVIDTTL flags or the deprecated ttl flag\n",
expErrJSON: "Error: use x509SVIDTTL and jwtSVIDTTL flags or the deprecated ttl flag\n",
},
{
name: "Invalid TTL and JwtSvidTtl",
args: []string{"-entryID", "entry-id", "-selector", "unix", "-parentID", "spiffe://example.org/parent", "-spiffeID", "spiffe://example.org/workload", "-ttl", "10", "-jwtSVIDTTL", "20"},
expErrPretty: "Error: use x509SVIDTTL and jwtSVIDTTL flags or the deprecated ttl flag\n",
expErrJSON: "Error: use x509SVIDTTL and jwtSVIDTTL flags or the deprecated ttl flag\n",
},
{
name: "Invalid TTL and both X509SvidTtl and JwtSvidTtl",
args: []string{"-entryID", "entry-id", "-selector", "unix", "-parentID", "spiffe://example.org/parent", "-spiffeID", "spiffe://example.org/workload", "-ttl", "10", "-x509SVIDTTL", "20", "-jwtSVIDTTL", "30"},
expErrPretty: "Error: use x509SVIDTTL and jwtSVIDTTL flags or the deprecated ttl flag\n",
expErrJSON: "Error: use x509SVIDTTL and jwtSVIDTTL flags or the deprecated ttl flag\n",
},
{
name: "Server error",
args: []string{"-entryID", "entry-id", "-spiffeID", "spiffe://example.org/workload", "-parentID", "spiffe://example.org/parent", "-selector", "unix:uid:1"},
@ -453,6 +495,58 @@ DNS name : ung1000
Admin : true
Hint : external
`, time.Unix(1552410266, 0).UTC()),
expOutJSON: fmt.Sprintf(`{
"results": [
{
"status": {
"code": 0,
"message": "OK"
},
"entry": %s
}
]
}`, entry0AdminJSON),
},
{
name: "Update succeeds using deprecated command line arguments",
args: []string{
"-entryID", "entry-id",
"-spiffeID", "spiffe://example.org/workload",
"-parentID", "spiffe://example.org/parent",
"-selector", "zebra:zebra:2000",
"-selector", "alpha:alpha:2000",
"-ttl", "60",
"-federatesWith", "spiffe://domaina.test",
"-federatesWith", "spiffe://domainb.test",
"-admin",
"-entryExpiry", "1552410266",
"-dns", "unu1000",
"-dns", "ung1000",
"-downstream",
"-hint", "external",
},
expReq: &entryv1.BatchUpdateEntryRequest{
Entries: []*types.Entry{entry5},
},
fakeResp: fakeRespOKFromCmd,
expOutPretty: fmt.Sprintf(`Entry ID : entry-id
SPIFFE ID : spiffe://example.org/workload
Parent ID : spiffe://example.org/parent
Revision : 0
Downstream : true
X509-SVID TTL : 60
JWT-SVID TTL : 30
Expiration time : %s
Selector : zebra:zebra:2000
Selector : alpha:alpha:2000
FederatesWith : spiffe://domaina.test
FederatesWith : spiffe://domainb.test
DNS name : unu1000
DNS name : ung1000
Admin : true
Hint : external
`, time.Unix(1552410266, 0).UTC()),
expOutJSON: fmt.Sprintf(`{
"results": [

View File

@ -13,7 +13,7 @@ import (
"github.com/spiffe/spire/proto/spire/common"
)
func printEntry(e *types.Entry, printf func(string, ...any) error) {
func printEntry(e *types.Entry, printf func(string, ...interface{}) error) {
_ = printf("Entry ID : %s\n", printableEntryID(e.Id))
_ = printf("SPIFFE ID : %s\n", protoToIDString(e.SpiffeId))
_ = printf("Parent ID : %s\n", protoToIDString(e.ParentId))

View File

@ -1,4 +1,5 @@
//go:build !windows
// +build !windows
package entry
@ -21,7 +22,7 @@ const (
-hint string
The entry hint, used to disambiguate entries with the same SPIFFE ID
-jwtSVIDTTL int
The lifetime, in seconds, for JWT-SVIDs issued based on this registration entry.
The lifetime, in seconds, for JWT-SVIDs issued based on this registration entry. Overrides ttl flag
-node
If set, this entry will be applied to matching nodes rather than workloads
-output value
@ -36,8 +37,10 @@ const (
The SPIFFE ID that this record represents
-storeSVID
A boolean value that, when set, indicates that the resulting issued SVID from this entry must be stored through an SVIDStore plugin
-ttl int
The lifetime, in seconds, for SVIDs issued based on this registration entry. This flag is deprecated in favor of x509SVIDTTL and jwtSVIDTTL and will be removed in a future version
-x509SVIDTTL int
The lifetime, in seconds, for x509-SVIDs issued based on this registration entry.
The lifetime, in seconds, for x509-SVIDs issued based on this registration entry. Overrides ttl flag
`
showUsage = `Usage of entry show:
-downstream
@ -81,7 +84,7 @@ const (
-hint string
The entry hint, used to disambiguate entries with the same SPIFFE ID
-jwtSVIDTTL int
The lifetime, in seconds, for JWT-SVIDs issued based on this registration entry.
The lifetime, in seconds, for JWT-SVIDs issued based on this registration entry. Overrides ttl flag
-output value
Desired output format (pretty, json); default: pretty.
-parentID string
@ -94,8 +97,10 @@ const (
The SPIFFE ID that this record represents
-storeSVID
A boolean value that, when set, indicates that the resulting issued SVID from this entry must be stored through an SVIDStore plugin
-ttl int
The lifetime, in seconds, for SVIDs issued based on this registration entry. This flag is deprecated in favor of x509SVIDTTL and jwtSVIDTTL and will be removed in a future version
-x509SVIDTTL int
The lifetime, in seconds, for x509-SVIDs issued based on this registration entry.
The lifetime, in seconds, for x509-SVIDs issued based on this registration entry. Overrides ttl flag
`
deleteUsage = `Usage of entry delete:
-entryID string
@ -108,25 +113,9 @@ const (
Path to the SPIRE Server API socket (default "/tmp/spire-server/private/api.sock")
`
countUsage = `Usage of entry count:
-downstream
A boolean value that, when set, indicates that the entry describes a downstream SPIRE server
-federatesWith value
SPIFFE ID of a trust domain an entry is federate with. Can be used more than once
-hint string
The Hint of the records to count (optional)
-matchFederatesWithOn string
The match mode used when filtering by federates with. Options: exact, any, superset and subset (default "superset")
-matchSelectorsOn string
The match mode used when filtering by selectors. Options: exact, any, superset and subset (default "superset")
-output value
Desired output format (pretty, json); default: pretty.
-parentID string
The Parent ID of the records to count
-selector value
A colon-delimited type:value selector. Can be used more than once
-socketPath string
Path to the SPIRE Server API socket (default "/tmp/spire-server/private/api.sock")
-spiffeID string
The SPIFFE ID of the records to count
`
)

View File

@ -2,7 +2,6 @@ package entry
import (
"bytes"
"context"
"os"
"path"
"testing"
@ -10,12 +9,13 @@ import (
"github.com/mitchellh/cli"
entryv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/entry/v1"
"github.com/spiffe/spire-api-sdk/proto/spire/api/types"
"github.com/spiffe/spire/cmd/spire-server/cli/common"
common_cli "github.com/spiffe/spire/pkg/common/cli"
"github.com/spiffe/spire/test/clitest"
"github.com/spiffe/spire/test/spiretest"
"github.com/spiffe/spire/test/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
@ -45,6 +45,7 @@ func TestParseEntryJSON(t *testing.T) {
}
for _, testCase := range testCases {
testCase := testCase
t.Run(testCase.name, func(t *testing.T) {
p := testCase.testDataPath
@ -154,7 +155,7 @@ func (e *entryTest) afterTest(t *testing.T) {
}
func (e *entryTest) args(extra ...string) []string {
return append([]string{clitest.AddrArg, e.addr}, extra...)
return append([]string{common.AddrArg, e.addr}, extra...)
}
type fakeEntryServer struct {
@ -241,7 +242,7 @@ func setupTest(t *testing.T, newClient func(*common_cli.Env) cli.Command) *entry
})
test := &entryTest{
addr: clitest.GetAddr(addr),
addr: common.GetAddr(addr),
stdin: stdin,
stdout: stdout,
stderr: stderr,

View File

@ -1,4 +1,5 @@
//go:build windows
// +build windows
package entry
@ -21,7 +22,7 @@ const (
-hint string
The entry hint, used to disambiguate entries with the same SPIFFE ID
-jwtSVIDTTL int
The lifetime, in seconds, for JWT-SVIDs issued based on this registration entry.
The lifetime, in seconds, for JWT-SVIDs issued based on this registration entry. Overrides ttl flag
-namedPipeName string
Pipe name of the SPIRE Server API named pipe (default "\\spire-server\\private\\api")
-node
@ -36,8 +37,10 @@ const (
The SPIFFE ID that this record represents
-storeSVID
A boolean value that, when set, indicates that the resulting issued SVID from this entry must be stored through an SVIDStore plugin
-ttl int
The lifetime, in seconds, for SVIDs issued based on this registration entry. This flag is deprecated in favor of x509SVIDTTL and jwtSVIDTTL and will be removed in a future version
-x509SVIDTTL int
The lifetime, in seconds, for x509-SVIDs issued based on this registration entry.
The lifetime, in seconds, for x509-SVIDs issued based on this registration entry. Overrides ttl flag
`
showUsage = `Usage of entry show:
-downstream
@ -81,7 +84,7 @@ const (
-hint string
The entry hint, used to disambiguate entries with the same SPIFFE ID
-jwtSVIDTTL int
The lifetime, in seconds, for JWT-SVIDs issued based on this registration entry.
The lifetime, in seconds, for JWT-SVIDs issued based on this registration entry. Overrides ttl flag
-namedPipeName string
Pipe name of the SPIRE Server API named pipe (default "\\spire-server\\private\\api")
-output value
@ -94,8 +97,10 @@ const (
The SPIFFE ID that this record represents
-storeSVID
A boolean value that, when set, indicates that the resulting issued SVID from this entry must be stored through an SVIDStore plugin
-ttl int
The lifetime, in seconds, for SVIDs issued based on this registration entry. This flag is deprecated in favor of x509SVIDTTL and jwtSVIDTTL and will be removed in a future version
-x509SVIDTTL int
The lifetime, in seconds, for x509-SVIDs issued based on this registration entry.
The lifetime, in seconds, for x509-SVIDs issued based on this registration entry. Overrides ttl flag
`
deleteUsage = `Usage of entry delete:
-entryID string
@ -108,25 +113,9 @@ const (
Desired output format (pretty, json); default: pretty.
`
countUsage = `Usage of entry count:
-downstream
A boolean value that, when set, indicates that the entry describes a downstream SPIRE server
-federatesWith value
SPIFFE ID of a trust domain an entry is federate with. Can be used more than once
-hint string
The Hint of the records to count (optional)
-matchFederatesWithOn string
The match mode used when filtering by federates with. Options: exact, any, superset and subset (default "superset")
-matchSelectorsOn string
The match mode used when filtering by selectors. Options: exact, any, superset and subset (default "superset")
-namedPipeName string
Pipe name of the SPIRE Server API named pipe (default "\\spire-server\\private\\api")
-output value
Desired output format (pretty, json); default: pretty.
-parentID string
The Parent ID of the records to count
-selector value
A colon-delimited type:value selector. Can be used more than once
-spiffeID string
The SPIFFE ID of the records to count
`
)

View File

@ -170,7 +170,7 @@ func bundleFromRawMessage(raw json.RawMessage, bundleFormat string, endpointTrus
return util.ParseBundle(bundle, bundleFormat, endpointTrustDomain)
}
func printFederationRelationship(fr *types.FederationRelationship, printf func(format string, args ...any) error) {
func printFederationRelationship(fr *types.FederationRelationship, printf func(format string, args ...interface{}) error) {
_ = printf("Trust domain : %s\n", fr.TrustDomain)
_ = printf("Bundle endpoint URL : %s\n", fr.BundleEndpointUrl)

View File

@ -11,9 +11,9 @@ import (
"github.com/spiffe/go-spiffe/v2/spiffeid"
trustdomainv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/trustdomain/v1"
"github.com/spiffe/spire-api-sdk/proto/spire/api/types"
"github.com/spiffe/spire/cmd/spire-server/cli/common"
common_cli "github.com/spiffe/spire/pkg/common/cli"
"github.com/spiffe/spire/pkg/common/pemutil"
"github.com/spiffe/spire/test/clitest"
"github.com/spiffe/spire/test/fakes/fakeserverca"
"github.com/spiffe/spire/test/spiretest"
"github.com/stretchr/testify/require"
@ -124,7 +124,7 @@ func (c *cmdTest) afterTest(t *testing.T) {
}
func (c *cmdTest) args(extra ...string) []string {
return append([]string{clitest.AddrArg, c.addr}, extra...)
return append([]string{common.AddrArg, c.addr}, extra...)
}
type fakeServer struct {
@ -222,7 +222,7 @@ func setupTest(t *testing.T, newClient func(*common_cli.Env) cli.Command) *cmdTe
})
test := &cmdTest{
addr: clitest.GetAddr(addr),
addr: common.GetAddr(addr),
stdin: stdin,
stdout: stdout,
stderr: stderr,
@ -241,7 +241,7 @@ func createBundle(t *testing.T, trustDomain string) (*types.Bundle, string) {
td := spiffeid.RequireTrustDomainFromString(trustDomain)
bundlePath := path.Join(t.TempDir(), "bundle.pem")
ca := fakeserverca.New(t, td, &fakeserverca.Options{})
require.NoError(t, os.WriteFile(bundlePath, pemutil.EncodeCertificates(ca.Bundle()), 0o600))
require.NoError(t, os.WriteFile(bundlePath, pemutil.EncodeCertificates(ca.Bundle()), 0600))
return &types.Bundle{
TrustDomain: td.Name(),
@ -253,13 +253,13 @@ func createBundle(t *testing.T, trustDomain string) (*types.Bundle, string) {
func createCorruptedBundle(t *testing.T) string {
bundlePath := path.Join(t.TempDir(), "bundle.pem")
require.NoError(t, os.WriteFile(bundlePath, []byte("corrupted-bundle"), 0o600))
require.NoError(t, os.WriteFile(bundlePath, []byte("corrupted-bundle"), 0600))
return bundlePath
}
func createJSONDataFile(t *testing.T, data string) string {
jsonDataFilePath := path.Join(t.TempDir(), "bundle.pem")
require.NoError(t, os.WriteFile(jsonDataFilePath, []byte(data), 0o600))
require.NoError(t, os.WriteFile(jsonDataFilePath, []byte(data), 0600))
return jsonDataFilePath
}

View File

@ -9,10 +9,9 @@ import (
"github.com/mitchellh/cli"
trustdomainv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/trustdomain/v1"
"github.com/spiffe/spire-api-sdk/proto/spire/api/types"
serverutil "github.com/spiffe/spire/cmd/spire-server/util"
"github.com/spiffe/spire/cmd/spire-server/util"
commoncli "github.com/spiffe/spire/pkg/common/cli"
"github.com/spiffe/spire/pkg/common/cliprinter"
"github.com/spiffe/spire/pkg/common/util"
"google.golang.org/grpc/codes"
)
@ -27,7 +26,7 @@ func NewCreateCommand() cli.Command {
}
func newCreateCommand(env *commoncli.Env) cli.Command {
return serverutil.AdaptCommand(env, &createCommand{env: env})
return util.AdaptCommand(env, &createCommand{env: env})
}
type createCommand struct {
@ -53,7 +52,7 @@ func (c *createCommand) AppendFlags(f *flag.FlagSet) {
cliprinter.AppendFlagWithCustomPretty(&c.printer, f, c.env, c.prettyPrintCreate)
}
func (c *createCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient serverutil.ServerClient) error {
func (c *createCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient util.ServerClient) error {
federationRelationships, err := getRelationships(c.config, c.path)
if err != nil {
return err
@ -72,7 +71,7 @@ func (c *createCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient
return c.printer.PrintProto(resp)
}
func (c *createCommand) prettyPrintCreate(env *commoncli.Env, results ...any) error {
func (c *createCommand) prettyPrintCreate(env *commoncli.Env, results ...interface{}) error {
createResp, ok := results[0].(*trustdomainv1.BatchCreateFederationRelationshipResponse)
if !ok || len(c.federationRelationships) < len(createResp.Results) {
return cliprinter.ErrInternalCustomPrettyFunc
@ -102,7 +101,7 @@ func (c *createCommand) prettyPrintCreate(env *commoncli.Env, results ...any) er
for _, r := range failed {
env.Println()
env.ErrPrintf("Failed to create the following federation relationship (code: %s, msg: %q):\n",
util.MustCast[codes.Code](r.Status.Code),
codes.Code(r.Status.Code),
r.Status.Message)
printFederationRelationship(r.FederationRelationship, env.ErrPrintf)
}

View File

@ -18,7 +18,7 @@ import (
"google.golang.org/grpc/codes"
)
func TestCreateHelp(t *testing.T) {
func TestCreatetHelp(t *testing.T) {
test := setupTest(t, newCreateCommand)
test.client.Help()

View File

@ -57,7 +57,7 @@ func (c *deleteCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient
return c.printer.PrintProto(resp)
}
func prettyPrintDelete(env *commoncli.Env, results ...any) error {
func prettyPrintDelete(env *commoncli.Env, results ...interface{}) error {
if deleteResp, ok := results[0].(*trustdomain.BatchDeleteFederationRelationshipResponse); ok && len(deleteResp.Results) > 0 {
result := deleteResp.Results[0]
switch result.Status.Code {

View File

@ -47,7 +47,7 @@ func (c *listCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient ut
return c.printer.PrintProto(resp)
}
func prettyPrintList(env *commoncli.Env, results ...any) error {
func prettyPrintList(env *commoncli.Env, results ...interface{}) error {
listResp, ok := results[0].(*trustdomainv1.ListFederationRelationshipsResponse)
if !ok {
return cliprinter.ErrInternalCustomPrettyFunc

View File

@ -63,6 +63,6 @@ func (c *refreshCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient
}
}
func prettyPrintRefresh(env *commoncli.Env, _ ...any) error {
func prettyPrintRefresh(env *commoncli.Env, _ ...interface{}) error {
return env.Println("Bundle refreshed")
}

View File

@ -59,7 +59,7 @@ func (c *showCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient ut
return c.printer.PrintProto(fr)
}
func (c *showCommand) prettyPrintShow(env *commoncli.Env, results ...any) error {
func (c *showCommand) prettyPrintShow(env *commoncli.Env, results ...interface{}) error {
fr, ok := results[0].(*prototypes.FederationRelationship)
if !ok {
return cliprinter.ErrInternalCustomPrettyFunc

View File

@ -9,10 +9,9 @@ import (
"github.com/mitchellh/cli"
trustdomainv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/trustdomain/v1"
"github.com/spiffe/spire-api-sdk/proto/spire/api/types"
serverutil "github.com/spiffe/spire/cmd/spire-server/util"
"github.com/spiffe/spire/cmd/spire-server/util"
commoncli "github.com/spiffe/spire/pkg/common/cli"
"github.com/spiffe/spire/pkg/common/cliprinter"
"github.com/spiffe/spire/pkg/common/util"
"google.golang.org/grpc/codes"
)
@ -22,7 +21,7 @@ func NewUpdateCommand() cli.Command {
}
func newUpdateCommand(env *commoncli.Env) cli.Command {
return serverutil.AdaptCommand(env, &updateCommand{env: env})
return util.AdaptCommand(env, &updateCommand{env: env})
}
type updateCommand struct {
@ -48,7 +47,7 @@ func (c *updateCommand) AppendFlags(f *flag.FlagSet) {
cliprinter.AppendFlagWithCustomPretty(&c.printer, f, c.env, c.prettyPrintUpdate)
}
func (c *updateCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient serverutil.ServerClient) error {
func (c *updateCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient util.ServerClient) error {
federationRelationships, err := getRelationships(c.config, c.path)
if err != nil {
return err
@ -67,7 +66,7 @@ func (c *updateCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient
return c.printer.PrintProto(resp)
}
func (c *updateCommand) prettyPrintUpdate(env *commoncli.Env, results ...any) error {
func (c *updateCommand) prettyPrintUpdate(env *commoncli.Env, results ...interface{}) error {
updateResp, ok := results[0].(*trustdomainv1.BatchUpdateFederationRelationshipResponse)
if !ok || len(c.federationRelationships) < len(updateResp.Results) {
return cliprinter.ErrInternalCustomPrettyFunc
@ -98,7 +97,7 @@ func (c *updateCommand) prettyPrintUpdate(env *commoncli.Env, results ...any) er
for _, r := range failed {
env.Println()
env.ErrPrintf("Failed to update the following federation relationship (code: %s, msg: %q):\n",
util.MustCast[codes.Code](r.Status.Code),
codes.Code(r.Status.Code),
r.Status.Message)
printFederationRelationship(r.FederationRelationship, env.ErrPrintf)
}

View File

@ -1,4 +1,5 @@
//go:build !windows
// +build !windows
package federation

View File

@ -1,4 +1,5 @@
//go:build windows
// +build windows
package federation

View File

@ -1,4 +1,5 @@
//go:build !windows
// +build !windows
package healthcheck

View File

@ -6,8 +6,8 @@ import (
"testing"
"github.com/mitchellh/cli"
"github.com/spiffe/spire/cmd/spire-server/cli/common"
common_cli "github.com/spiffe/spire/pkg/common/cli"
"github.com/spiffe/spire/test/clitest"
"github.com/spiffe/spire/test/spiretest"
"github.com/stretchr/testify/suite"
"google.golang.org/grpc"
@ -58,24 +58,24 @@ func (s *HealthCheckSuite) TestBadFlags() {
}
func (s *HealthCheckSuite) TestFailsIfEndpointDoesNotExist() {
code := s.cmd.Run([]string{clitest.AddrArg, clitest.AddrValue})
code := s.cmd.Run([]string{common.AddrArg, common.AddrValue})
s.NotEqual(0, code, "exit code")
s.Equal("", s.stdout.String(), "stdout")
spiretest.AssertHasPrefix(s.T(), s.stderr.String(), "Error: server is unhealthy: unable to determine health\n")
spiretest.AssertHasPrefix(s.T(), s.stderr.String(), common.AddrError)
}
func (s *HealthCheckSuite) TestFailsIfEndpointDoesNotExistVerbose() {
code := s.cmd.Run([]string{clitest.AddrArg, clitest.AddrValue, "-verbose"})
code := s.cmd.Run([]string{common.AddrArg, common.AddrValue, "-verbose"})
s.NotEqual(0, code, "exit code")
s.Equal("Checking server health...\n", s.stdout.String(), "stdout")
spiretest.AssertHasPrefix(s.T(), s.stderr.String(), "Failed to check health: "+clitest.AddrError)
s.Equal("", s.stdout.String(), "stdout")
spiretest.AssertHasPrefix(s.T(), s.stderr.String(), common.AddrError)
}
func (s *HealthCheckSuite) TestSucceedsIfServingStatusServing() {
addr := spiretest.StartGRPCServer(s.T(), func(srv *grpc.Server) {
grpc_health_v1.RegisterHealthServer(srv, withStatus(grpc_health_v1.HealthCheckResponse_SERVING))
})
code := s.cmd.Run([]string{clitest.AddrArg, clitest.GetAddr(addr)})
code := s.cmd.Run([]string{common.AddrArg, common.GetAddr(addr)})
s.Equal(0, code, "exit code")
s.Equal("Server is healthy.\n", s.stdout.String(), "stdout")
s.Equal("", s.stderr.String(), "stderr")
@ -85,7 +85,7 @@ func (s *HealthCheckSuite) TestSucceedsIfServingStatusServingVerbose() {
addr := spiretest.StartGRPCServer(s.T(), func(srv *grpc.Server) {
grpc_health_v1.RegisterHealthServer(srv, withStatus(grpc_health_v1.HealthCheckResponse_SERVING))
})
code := s.cmd.Run([]string{clitest.AddrArg, clitest.GetAddr(addr), "-verbose"})
code := s.cmd.Run([]string{common.AddrArg, common.GetAddr(addr), "-verbose"})
s.Equal(0, code, "exit code")
s.Equal(`Checking server health...
Server is healthy.
@ -97,7 +97,7 @@ func (s *HealthCheckSuite) TestFailsIfServiceStatusOther() {
addr := spiretest.StartGRPCServer(s.T(), func(srv *grpc.Server) {
grpc_health_v1.RegisterHealthServer(srv, withStatus(grpc_health_v1.HealthCheckResponse_NOT_SERVING))
})
code := s.cmd.Run([]string{clitest.AddrArg, clitest.GetAddr(addr), "-verbose"})
code := s.cmd.Run([]string{common.AddrArg, common.GetAddr(addr), "-verbose"})
s.NotEqual(0, code, "exit code")
s.Equal(`Checking server health...
`, s.stdout.String(), "stdout")

View File

@ -1,4 +1,5 @@
//go:build windows
// +build windows
package healthcheck

Some files were not shown because too many files have changed in this diff Show More