Compare commits
213 Commits
Author | SHA1 | Date |
---|---|---|
|
ed8a425996 | |
|
b5de14a841 | |
|
c524170ad7 | |
|
92ebcfb887 | |
|
9ad7cdc4c5 | |
|
89b95d1dbb | |
|
05a4dcf821 | |
|
bc6111e42e | |
|
76b8ec7106 | |
|
010979d46a | |
|
cb290deb27 | |
|
6ab5cf06ed | |
|
e994f567ec | |
|
ce08f198ec | |
|
ed817db2e0 | |
|
dcf2f8b3c7 | |
|
43801c56ef | |
|
28bfbefc73 | |
|
c8b21bf055 | |
|
320eb61bd1 | |
|
00a27527e5 | |
|
05f138bf46 | |
|
2df4a0f700 | |
|
ad7818739a | |
|
8493e798f0 | |
|
b3f1fbb6ce | |
|
f3cd008409 | |
|
b4c23d42f4 | |
|
7de9848a21 | |
|
2262f83b0f | |
|
16e8c66468 | |
|
0df3793700 | |
|
05c70d94b7 | |
|
0a817bc6d1 | |
|
8b3d392f9a | |
|
fcf0e0a603 | |
|
78f9741b08 | |
|
531c74ac7e | |
|
07d5927ae6 | |
|
52e81cd08e | |
|
1e5de4c7ad | |
|
b86c06ab7a | |
|
5e07359ea8 | |
|
e78a082234 | |
|
1b4de1d825 | |
|
b47c28e9f0 | |
|
583360bf7f | |
|
e9bf593cc3 | |
|
b26cedb69d | |
|
fdf8efa0b0 | |
|
e9c2382fc0 | |
|
c91bc59029 | |
|
ed615ddb47 | |
|
175ac1c970 | |
|
104070e565 | |
|
c346b6cf8b | |
|
8661531afd | |
|
75986770f4 | |
|
61d9a4bc73 | |
|
26b5549d2f | |
|
acf4cf70d1 | |
|
ad111b5fe2 | |
|
82f8ff97e8 | |
|
e16f53c697 | |
|
8481a0c0f2 | |
|
48304fdef8 | |
|
3874f2a983 | |
|
e9f686d5fe | |
|
e2bbdf6686 | |
|
b7ee203373 | |
|
9b48e65db4 | |
|
cfe8bd7e90 | |
|
af9faf932f | |
|
2094b33818 | |
|
63a4b30fd6 | |
|
b0103838b8 | |
|
6941cac981 | |
|
4cbb9af8fc | |
|
815259034a | |
|
e61f38793d | |
|
bd211870bd | |
|
b605fd9ad3 | |
|
f9aaf4e8c7 | |
|
e75b363828 | |
|
9d199dddf2 | |
|
d2be62b687 | |
|
842214896a | |
|
76da0f26b0 | |
|
70e5f1c2af | |
|
4b7bccc2a3 | |
|
b28fce932a | |
|
f49b72a425 | |
|
231f6f6927 | |
|
adc72189c6 | |
|
19fb06495a | |
|
abc7bba22c | |
|
88c3d91d16 | |
|
3791df8ddf | |
|
13b6b67aec | |
|
46181c3d21 | |
|
3d26c01823 | |
|
642da310c2 | |
|
2c3f7a8f49 | |
|
b02002ca2a | |
|
eb1ee40fce | |
|
13a3ff4bf1 | |
|
54c7f2416c | |
|
b0b36a141f | |
|
53e436c657 | |
|
00d745e652 | |
|
492a4a14c1 | |
|
8e065e45a6 | |
|
b482bf568c | |
|
cca2f86d0f | |
|
fe27b73441 | |
|
df04e9d9df | |
|
9d55005a05 | |
|
e5f4d62973 | |
|
bdacf2697b | |
|
58b2cc26c7 | |
|
ef5d1e1443 | |
|
a1b932b974 | |
|
ba5e1ac568 | |
|
2c25c3c17d | |
|
9badbff4f1 | |
|
d7595316b2 | |
|
3d6b44de6e | |
|
bdbd708541 | |
|
c9dccac62d | |
|
bfaf385376 | |
|
11e4988313 | |
|
d84eab9acb | |
|
88c20a54e5 | |
|
d168d2bcb8 | |
|
7928996c10 | |
|
7dbdf174e3 | |
|
a5c9644908 | |
|
846a20eb52 | |
|
5b5fd0cd10 | |
|
695ee4b8fe | |
|
75b0c607e0 | |
|
baa166b47c | |
|
9002883239 | |
|
0a8bfaae2f | |
|
2d97330b2a | |
|
9158b9b720 | |
|
440d6b93b6 | |
|
5906f67bc3 | |
|
ad80394a58 | |
|
5175fa8061 | |
|
c750d8a094 | |
|
71e9295008 | |
|
f8ca6caf88 | |
|
0c7fe9eeb2 | |
|
86f63d4237 | |
|
04442dead8 | |
|
b5487b9daf | |
|
663637a69e | |
|
ec7afc4cdb | |
|
400cc54f08 | |
|
1edbf1ae3b | |
|
16f9f1cf60 | |
|
f448ddc263 | |
|
9a668d8f4f | |
|
4edaeb28fe | |
|
6efb7db424 | |
|
e696a8fc81 | |
|
af8bdf37ce | |
|
78cc94a49a | |
|
f4e031d856 | |
|
a9e78f117f | |
|
4f09c7fc47 | |
|
9decda1481 | |
|
a32ab3a4d8 | |
|
527d0d9752 | |
|
a20b3b3b82 | |
|
3e6ffafdc2 | |
|
20b3c1b1c9 | |
|
c8c1f118c1 | |
|
02b48de5b2 | |
|
04cc3b4644 | |
|
dabbd6d5ae | |
|
cbc47ce215 | |
|
a655a08f6e | |
|
4e9a2c48fa | |
|
17feee14bc | |
|
9b4e7f62f9 | |
|
a8f2b820f2 | |
|
866d118a9b | |
|
20f86fc272 | |
|
799a943716 | |
|
1cf9f72ce3 | |
|
21b0654425 | |
|
efcb220c42 | |
|
60b630aa0a | |
|
2f286ad0c6 | |
|
81569fd9f4 | |
|
5804474bc9 | |
|
e1fa57ac55 | |
|
709ae824be | |
|
1ebc76dc4b | |
|
ce7845d25e | |
|
c1bc55aa78 | |
|
ea963bb161 | |
|
c35aa65cfa | |
|
091d297fef | |
|
be16fdda06 | |
|
3f763f012c | |
|
36a5e09b09 | |
|
32b0699b75 | |
|
601f920f7e | |
|
fe38d8302d | |
|
7ce542909b |
|
@ -5,6 +5,14 @@ updates:
|
|||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 5
|
||||
groups:
|
||||
k8s.io:
|
||||
patterns:
|
||||
- "k8s.io/*"
|
||||
- package-ecosystem: gomod
|
||||
directory: /demo/greeter
|
||||
schedule:
|
||||
interval: daily
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
|
|
|
@ -18,9 +18,9 @@ jobs:
|
|||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.20.1
|
||||
go-version-file: 'go.mod'
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
|
@ -30,7 +30,7 @@ jobs:
|
|||
- name: Build image
|
||||
run: make docker-build
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@v3.0.0
|
||||
uses: docker/login-action@v3.4.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
|
|
|
@ -2,8 +2,6 @@ name: PR Build
|
|||
on:
|
||||
pull_request: {}
|
||||
workflow_dispatch: {}
|
||||
env:
|
||||
GO_VERSION: 1.20.1
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
|
@ -13,9 +11,9 @@ jobs:
|
|||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
go-version-file: 'go.mod'
|
||||
- name: Lint
|
||||
run: make lint
|
||||
|
||||
|
@ -29,9 +27,9 @@ jobs:
|
|||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
go-version-file: 'go.mod'
|
||||
- name: Run unit tests
|
||||
run: make test
|
||||
|
||||
|
@ -45,9 +43,9 @@ jobs:
|
|||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
go-version-file: 'go.mod'
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
|
@ -57,7 +55,7 @@ jobs:
|
|||
- name: Export images
|
||||
run: tar -czvf images.tar.gz *-image.tar
|
||||
- name: Archive image
|
||||
uses: actions/upload-artifact@v3.1.3
|
||||
uses: actions/upload-artifact@v4.6.2
|
||||
with:
|
||||
name: images
|
||||
path: images.tar.gz
|
||||
|
@ -76,7 +74,7 @@ jobs:
|
|||
- name: Install regctl
|
||||
uses: regclient/actions/regctl-installer@main
|
||||
- name: Download archived image
|
||||
uses: actions/download-artifact@v3.0.2
|
||||
uses: actions/download-artifact@v4.3.0
|
||||
with:
|
||||
name: images
|
||||
path: .
|
||||
|
|
|
@ -3,7 +3,6 @@ on:
|
|||
push:
|
||||
tags:
|
||||
- 'v[0-9].[0-9]+.[0-9]+'
|
||||
|
||||
jobs:
|
||||
build-image:
|
||||
runs-on: ubuntu-22.04
|
||||
|
@ -15,9 +14,9 @@ jobs:
|
|||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.20.1
|
||||
go-version-file: 'go.mod'
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
|
@ -27,7 +26,7 @@ jobs:
|
|||
- name: Export image
|
||||
run: tar -czvf images.tar.gz *-image.tar
|
||||
- name: Archive image
|
||||
uses: actions/upload-artifact@v3.1.3
|
||||
uses: actions/upload-artifact@v4.6.2
|
||||
with:
|
||||
name: images
|
||||
path: images.tar.gz
|
||||
|
@ -46,7 +45,7 @@ jobs:
|
|||
- name: Install regctl
|
||||
uses: regclient/actions/regctl-installer@main
|
||||
- name: Download archived image
|
||||
uses: actions/download-artifact@v3.0.2
|
||||
uses: actions/download-artifact@v4.3.0
|
||||
with:
|
||||
name: images
|
||||
path: .
|
||||
|
@ -75,12 +74,12 @@ jobs:
|
|||
- name: Install regctl
|
||||
uses: regclient/actions/regctl-installer@main
|
||||
- name: Download archived image
|
||||
uses: actions/download-artifact@v3.0.2
|
||||
uses: actions/download-artifact@v4.3.0
|
||||
with:
|
||||
name: images
|
||||
path: .
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@v3.0.0
|
||||
uses: docker/login-action@v3.4.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
1.23.4
|
|
@ -1,24 +1,33 @@
|
|||
run:
|
||||
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||
deadline: 10m
|
||||
timeout: 12m
|
||||
|
||||
linters:
|
||||
enable:
|
||||
- bodyclose
|
||||
- depguard
|
||||
- durationcheck
|
||||
- errorlint
|
||||
- gofmt
|
||||
- goimports
|
||||
- revive
|
||||
- gosec
|
||||
- misspell
|
||||
- nakedret
|
||||
- nilerr
|
||||
- unconvert
|
||||
- unparam
|
||||
- intrange
|
||||
- whitespace
|
||||
- gocritic
|
||||
- wastedassign
|
||||
- nolintlint
|
||||
|
||||
linters-settings:
|
||||
govet:
|
||||
enable:
|
||||
- nilness
|
||||
- sortslice
|
||||
- unusedwrite
|
||||
revive:
|
||||
# minimal confidence for issues, default is 0.8
|
||||
min-confidence: 0.0
|
||||
|
|
|
@ -5,3 +5,10 @@ v3.2.0: .scripts/kubebuilder create api --group spire --version v1alpha1 --kind
|
|||
v3.2.0: .scripts/kubebuilder create webhook --programmatic-validation --kind ClusterFederatedTrustDomain --version v1alpha1 --group spire
|
||||
v3.2.0: .scripts/kubebuilder create webhook --programmatic-validation --kind ClusterSPIFFEID --version v1alpha1 --group spire
|
||||
v3.3.0: .scripts/kubebuilder create api --resource --controller --group spire --version v1alpha1 --namespaced=false --kind ClusterStaticEntry
|
||||
v3.12.0: ./.scripts/kubebuilder init --domain spiffe.io --owner SPIRE\ Authors --project-name spire-controller-manager --skip-go-version-check --plugins=go/v4
|
||||
v3.12.0: .scripts/kubebuilder create api --resource --controller --group spire --version v1alpha1 --namespaced=false --kind ClusterSPIFFEID
|
||||
v3.12.0: .scripts/kubebuilder create api --resource --controller --group spire --version v1alpha1 --namespaced=false --kind ClusterFederatedTrustDomain
|
||||
v3.12.0: .scripts/kubebuilder create api --group spire --version v1alpha1 --kind ControllerManagerConfig --resource --controller=false --make=false
|
||||
v3.12.0: .scripts/kubebuilder create api --resource --controller --group spire --version v1alpha1 --namespaced=false --kind ClusterStaticEntry
|
||||
v3.12.0: .scripts/kubebuilder create webhook --programmatic-validation --kind ClusterFederatedTrustDomain --version v1alpha1 --group spire
|
||||
v3.12.0: .scripts/kubebuilder create webhook --programmatic-validation --kind ClusterSPIFFEID --version v1alpha1 --group spire
|
||||
|
|
|
@ -32,7 +32,7 @@ case "${ARCH1}" in
|
|||
x86_64)
|
||||
ARCH2=amd64
|
||||
;;
|
||||
aarch64)
|
||||
aarch64|arm64)
|
||||
ARCH2=arm64
|
||||
;;
|
||||
*)
|
||||
|
|
|
@ -8,7 +8,7 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
|||
|
||||
KUBEBUILDERHIST="${DIR}"/../.kubebuilder-hist
|
||||
|
||||
KUBEBUILDERVER="v3.3.0"
|
||||
KUBEBUILDERVER="v3.12.0"
|
||||
KUBEBUILDERBASE="${BUILDDIR}/kubebuilder"
|
||||
KUBEBUILDERDIR="${KUBEBUILDERBASE}/${KUBEBUILDERVER}"
|
||||
KUBEBUILDERBIN="${KUBEBUILDERDIR}/kubebuilder"
|
||||
|
|
107
CHANGELOG.md
107
CHANGELOG.md
|
@ -1,5 +1,110 @@
|
|||
# Changelog
|
||||
|
||||
## [0.6.2] - 2025-04-17
|
||||
|
||||
### Added
|
||||
|
||||
- Support `staticManifestPath`: watch a directory for CRs instead of using Kubernetes API (#411)
|
||||
|
||||
## [0.6.1] - 2025-02-14
|
||||
|
||||
### Added
|
||||
|
||||
- Support for configuring the log level (#388, #464)
|
||||
- New metrics to track `ClusterStaticEntry` failures (#387)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Failed controller upgrade when webhook certificate is expired (#450)
|
||||
|
||||
### Updated
|
||||
|
||||
- Minor documentation changes (#435, #443)
|
||||
- Version used in migration guide (#465)
|
||||
|
||||
## [0.6.0] - 2024-10-03
|
||||
|
||||
<font size='7'>:rotating_light: ***PLEASE READ BEFORE UPGRADING*** :rotating_light:</font>
|
||||
|
||||
This version contains changes in the `ClusterSPIFFEID` CRD. Before upgrading you __MUST__ do the following:
|
||||
|
||||
- Update the CRD in your cluster (see [here](./config/crd/bases/spire.spiffe.io_clusterspiffeids.yaml)).
|
||||
|
||||
### Added
|
||||
|
||||
- Hint field to the ClusterSPIFFEID CRD that controls the hint on resulting entries (#416)
|
||||
- Fallback field to the ClusterSPIFFEID CRD which causes the CR to only apply if no other non-fallback CRs have been applied to a given pod (#415)
|
||||
- Missing documentation for the className on the ClusterFederatedTrustDomain CRD (#413)
|
||||
|
||||
## [0.5.0] - 2024-04-10
|
||||
|
||||
<font size='7'>:rotating_light: ***PLEASE READ BEFORE UPGRADING*** :rotating_light:</font>
|
||||
|
||||
This version contains changes in the `ClusterStaticEntry` CRD. Before upgrading you __MUST__ do the following:
|
||||
|
||||
- Update the CRD in your cluster (see [here](.config/crd/bases/spire.spiffe.io_clusterstaticentries.yaml)).
|
||||
|
||||
### Added
|
||||
|
||||
- Support for `storeSVID` on ClusterStaticEntry (#304)
|
||||
- Support for more than one spire-controller-manager managing entries against a single SPIRE server cluster via entry prefixes (#325)
|
||||
|
||||
## [0.4.4] - 2024-04-05
|
||||
|
||||
### Security
|
||||
|
||||
- Updated Golang to 1.21.9 to address CVE-2023-45288 (#338)
|
||||
|
||||
## [0.4.3] - 2024-02-22
|
||||
|
||||
### Added
|
||||
|
||||
- Ability to selectively choose which CRDs to reconcile (#297)
|
||||
|
||||
### Changed
|
||||
|
||||
- Join token novelty entries are ignored during entry reconciliation (#306)
|
||||
|
||||
## [0.4.2] - 2024-01-24
|
||||
|
||||
### Added
|
||||
|
||||
- Process-wide support for customizing the parent ID template for workload registration (#289)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Failed controller startup when webhook was disabled via ENABLE_WEBHOOKS=false (#294)
|
||||
|
||||
## [0.4.1] - 2024-01-17
|
||||
|
||||
### Added
|
||||
|
||||
- Support for caching multiple namespaces instead of one or all (#271,#286)
|
||||
- Support for expanding environment variables in the controller configuration (#256)
|
||||
- Support for disabling webhooks by setting the environment variable ENABLE_WEBHOOKS=false (#234)
|
||||
|
||||
## [0.4.0] - 2023-11-02
|
||||
|
||||
<font size='7'>:rotating_light: ***PLEASE READ BEFORE UPGRADING*** :rotating_light:</font>
|
||||
|
||||
This version contains changes in the `ClusterSPIFFEID` CRD, `ClusterFederatedTrustDomain` CRD and `ClusterStaticEntry` CRD. Before upgrading you __MUST__ do the following, in order:
|
||||
|
||||
- Update those CRDs into your cluster (see [here](./config/crd/bases/spire.spiffe.io_clusterspiffeids.yaml), [here](./config/crd/bases/spire.spiffe.io_clusterfederatedtrustdomains.yaml) and [here](.config/crd/bases/spire.spiffe.io_clusterstaticentries.yaml)).
|
||||
- Update the `manager-role` ClusterRole, which includes additional permissions for `endpoints` CRD (see [here](./config/rbac/role.yaml))
|
||||
|
||||
### Security
|
||||
|
||||
- Updated to google.golang.org/grpc v1.59.0 to address CVE-2023-44487 (#231)
|
||||
|
||||
### Added
|
||||
|
||||
- ClusterSPIFFEID CRD support for DNS name auto-population (#122)
|
||||
- Support for multiple SPIRE clusters running in the same K8S cluster using ClassName's (#230)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Missing status subresource definitions (#223)
|
||||
|
||||
## [0.3.0] - 2023-09-14
|
||||
|
||||
<font size='7'>:rotating_light: ***PLEASE READ BEFORE UPGRADING*** :rotating_light:</font>
|
||||
|
@ -7,7 +112,7 @@
|
|||
This version contains changes in the `ClusterSPIFFEID` CRD. It also adds a new `ClusterStaticEntry` CRD. Before upgrading you __MUST__ do the following, in order:
|
||||
|
||||
- Update/install those CRDs into your cluster (see [here](./config/crd/bases/spire.spiffe.io_clusterstaticentries.yaml) and [here](./config/crd/bases/spire.spiffe.io_clusterspiffeids.yaml)).
|
||||
- Update the the `manager-role` ClusterRole, which includes additional permissions for the new `ClusterStaticEntry` CRD (see [here](./config/rbac/role.yaml))
|
||||
- Update the `manager-role` ClusterRole, which includes additional permissions for the new `ClusterStaticEntry` CRD (see [here](./config/rbac/role.yaml))
|
||||
|
||||
### Added
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
* @azdagron @MarcosDY
|
||||
* @azdagron @MarcosDY @kfox1111
|
||||
|
|
18
Dockerfile
18
Dockerfile
|
@ -1,26 +1,28 @@
|
|||
ARG goversion
|
||||
|
||||
# Build the manager binary
|
||||
FROM --platform=${BUILDPLATFORM} golang:1.20.1-alpine as base
|
||||
FROM --platform=${BUILDPLATFORM} golang:${goversion}-alpine AS base
|
||||
WORKDIR /workspace
|
||||
# Copy the Go Modules manifests
|
||||
COPY go.* ./
|
||||
# cache deps before building and copying source so that we don't need to re-download as much
|
||||
# Cache deps before building and copying source so that we don't need to re-download as much
|
||||
# and so that source changes don't invalidate our downloaded layer
|
||||
RUN --mount=type=cache,target=/go/pkg/mod go mod download
|
||||
|
||||
# Copy the go source
|
||||
COPY main.go main.go
|
||||
COPY cmd/main.go cmd/main.go
|
||||
COPY api/ api/
|
||||
COPY controllers/ controllers/
|
||||
COPY internal/ internal/
|
||||
COPY pkg/ pkg/
|
||||
|
||||
# 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.1.2
|
||||
FROM --platform=${BUILDPLATFORM} tonistiigi/xx@sha256:9dde7edeb9e4a957ce78be9f8c0fbabe0129bf5126933cd3574888f443731cda AS xx
|
||||
# crane digest tonistiigi/xx:1.3.0
|
||||
FROM --platform=${BUILDPLATFORM} tonistiigi/xx@sha256:904fe94f236d36d65aeb5a2462f88f2c537b8360475f6342e7599194f291fb7e AS xx
|
||||
|
||||
# Build
|
||||
FROM --platform=${BUILDPLATFORM} base as builder
|
||||
FROM --platform=${BUILDPLATFORM} base AS builder
|
||||
ARG TARGETPLATFORM
|
||||
ARG TARGETARCH
|
||||
ENV CGO_ENABLED=0
|
||||
|
@ -28,7 +30,7 @@ COPY --link --from=xx / /
|
|||
RUN xx-go --wrap
|
||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
--mount=type=cache,target=/go/pkg/mod \
|
||||
go build -o bin/spire-controller-manager main.go
|
||||
go build -o bin/spire-controller-manager cmd/main.go
|
||||
|
||||
# Use distroless as minimal base image to package the manager binary
|
||||
# Refer to https://github.com/GoogleContainerTools/distroless for more details
|
||||
|
|
99
Makefile
99
Makefile
|
@ -5,7 +5,7 @@ DIR := ${CURDIR}
|
|||
# Image URL to use all building/pushing image targets
|
||||
IMG ?= ghcr.io/spiffe/spire-controller-manager:devel
|
||||
# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
|
||||
ENVTEST_K8S_VERSION = 1.24
|
||||
ENVTEST_K8S_VERSION = 1.28.0
|
||||
|
||||
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
|
||||
ifeq (,$(shell go env GOBIN))
|
||||
|
@ -14,6 +14,12 @@ else
|
|||
GOBIN=$(shell go env GOBIN)
|
||||
endif
|
||||
|
||||
# CONTAINER_TOOL defines the container tool to be used for building images.
|
||||
# Be aware that the target commands are only tested with Docker which is
|
||||
# scaffolded by default. However, you might want to replace it to use other
|
||||
# tools. (i.e. podman)
|
||||
CONTAINER_TOOL ?= docker
|
||||
|
||||
# Setting SHELL to bash allows bash commands to be executed by recipes.
|
||||
# This is a requirement for 'setup-envtest.sh' in the test target.
|
||||
# Options are set to exit when a recipe line exits non-zero or a piped command fails.
|
||||
|
@ -27,7 +33,7 @@ all: build
|
|||
|
||||
# The help target prints out all targets with their descriptions organized
|
||||
# beneath their categories. The categories are represented by '##@' and the
|
||||
# target descriptions by '##'. The awk commands is responsible for reading the
|
||||
# target descriptions by '##'. The awk command is responsible for reading the
|
||||
# entire set of makefiles included in this invocation, looking for lines of the
|
||||
# file as xyz: ## something, and then pretty-format the target and help. Then,
|
||||
# if there's a line with ##@ something, that gets pretty-printed as a category.
|
||||
|
@ -71,9 +77,10 @@ endif
|
|||
|
||||
##@ Vars
|
||||
|
||||
go_version := $(shell cat .go-version)
|
||||
build_dir := $(DIR)/.build/$(os1)-$(arch1)
|
||||
|
||||
golangci_lint_version = v1.51.2
|
||||
golangci_lint_version = v1.60.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
|
||||
|
@ -109,7 +116,7 @@ vet: ## Run go vet against code.
|
|||
|
||||
.PHONY: test
|
||||
test: manifests generate fmt vet envtest ## Run tests.
|
||||
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test ./... -coverprofile cover.out
|
||||
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test ./... -coverprofile cover.out
|
||||
|
||||
##@ Code cleanliness
|
||||
|
||||
|
@ -124,7 +131,7 @@ lint-code: $(golangci_lint_bin)
|
|||
.PHONY: build
|
||||
build: $(addprefix bin,/$(BINARIES)) ## Build manager binary.
|
||||
|
||||
bin/%: main.go generate fmt vet FORCE
|
||||
bin/%: cmd/main.go generate fmt vet FORCE
|
||||
go build -o $@ $<
|
||||
|
||||
.PHONY: run
|
||||
|
@ -133,15 +140,35 @@ run: build ## Run a controller from your host.
|
|||
|
||||
.PHONY: container-builder
|
||||
container-builder: ## Create a buildx node to create crossplatform images.
|
||||
docker buildx create --platform $(PLATFORMS) --name container-builder --node container-builder0 --use
|
||||
$(CONTAINER_TOOL) buildx create --platform $(PLATFORMS) --name container-builder --node container-builder0 --use
|
||||
|
||||
.PHONY: docker-build
|
||||
docker-build: $(addsuffix -image.tar,$(BINARIES)) ## Build docker image with the manager.
|
||||
|
||||
# PLATFORMS defines the target platforms for the manager image be built to provide support to multiple
|
||||
# architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to:
|
||||
# - be able to use docker buildx. More info: https://docs.docker.com/build/buildx/
|
||||
# - have enabled BuildKit. More info: https://docs.docker.com/develop/develop-images/build_enhancements/
|
||||
# - be able to push the image to your registry (i.e. if you do not set a valid value via IMG=<myregistry/image:<tag>> then the export will fail)
|
||||
# To adequately provide solutions that are compatible with multiple platforms, you should consider using this option.
|
||||
PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le
|
||||
# TODO: may we support this?
|
||||
.PHONY: docker-buildx
|
||||
docker-buildx: ## Build and push docker image for the manager for cross-platform support
|
||||
# copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile
|
||||
sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross
|
||||
- $(CONTAINER_TOOL) buildx create --name project-v3-builder
|
||||
$(CONTAINER_TOOL) buildx use project-v3-builder
|
||||
- $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross .
|
||||
- $(CONTAINER_TOOL) buildx rm project-v3-builder
|
||||
rm Dockerfile.cross
|
||||
|
||||
|
||||
spire-controller-manager-image.tar: Dockerfile FORCE | container-builder
|
||||
docker buildx build \
|
||||
$(CONTAINER_TOOL) buildx build \
|
||||
--platform $(PLATFORMS) \
|
||||
--target spire-controller-manager \
|
||||
--build-arg goversion=$(go_version) \
|
||||
-o type=oci,dest=$@ \
|
||||
.
|
||||
|
||||
|
@ -161,41 +188,55 @@ endif
|
|||
|
||||
.PHONY: install
|
||||
install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config.
|
||||
$(KUSTOMIZE) build config/crd | kubectl apply -f -
|
||||
$(KUSTOMIZE) build config/crd | $(KUBECTL) apply -f -
|
||||
|
||||
.PHONY: uninstall
|
||||
uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
|
||||
$(KUSTOMIZE) build config/crd | kubectl delete --ignore-not-found=$(ignore-not-found) -f -
|
||||
$(KUSTOMIZE) build config/crd | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f -
|
||||
|
||||
.PHONY: deploy
|
||||
deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config.
|
||||
cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
|
||||
$(KUSTOMIZE) build config/default | kubectl apply -f -
|
||||
$(KUSTOMIZE) build config/default | $(KUBECTL) apply -f -
|
||||
|
||||
.PHONY: undeploy
|
||||
undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
|
||||
$(KUSTOMIZE) build config/default | kubectl delete --ignore-not-found=$(ignore-not-found) -f -
|
||||
$(KUSTOMIZE) build config/default | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f -
|
||||
|
||||
CONTROLLER_GEN = $(shell pwd)/bin/controller-gen
|
||||
.PHONY: controller-gen
|
||||
controller-gen: ## Download controller-gen locally if necessary.
|
||||
$(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.11.1)
|
||||
##@ Build Dependencies
|
||||
|
||||
## Location to install dependencies to
|
||||
LOCALBIN ?= $(shell pwd)/bin
|
||||
$(LOCALBIN):
|
||||
mkdir -p $(LOCALBIN)
|
||||
|
||||
## Tool Binaries
|
||||
KUBECTL ?= kubectl
|
||||
KUSTOMIZE ?= $(LOCALBIN)/kustomize
|
||||
CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
|
||||
ENVTEST ?= $(LOCALBIN)/setup-envtest
|
||||
|
||||
## Tool Versions
|
||||
KUSTOMIZE_VERSION ?= v5.1.1
|
||||
CONTROLLER_TOOLS_VERSION ?= v0.14.0
|
||||
|
||||
KUSTOMIZE = $(shell pwd)/bin/kustomize
|
||||
.PHONY: kustomize
|
||||
kustomize: ## Download kustomize locally if necessary.
|
||||
$(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v3@v3.8.7)
|
||||
kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. If wrong version is installed, it will be removed before downloading.
|
||||
$(KUSTOMIZE): $(LOCALBIN)
|
||||
@if test -x $(LOCALBIN)/kustomize && ! $(LOCALBIN)/kustomize version | grep -q $(KUSTOMIZE_VERSION); then \
|
||||
echo "$(LOCALBIN)/kustomize version is not expected $(KUSTOMIZE_VERSION). Removing it before installing."; \
|
||||
rm -rf $(LOCALBIN)/kustomize; \
|
||||
fi
|
||||
test -s $(LOCALBIN)/kustomize || GOBIN=$(LOCALBIN) GO111MODULE=on go install sigs.k8s.io/kustomize/kustomize/v5@$(KUSTOMIZE_VERSION)
|
||||
|
||||
.PHONY: controller-gen
|
||||
controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. If wrong version is installed, it will be overwritten.
|
||||
$(CONTROLLER_GEN): $(LOCALBIN)
|
||||
test -s $(LOCALBIN)/controller-gen && $(LOCALBIN)/controller-gen --version | grep -q $(CONTROLLER_TOOLS_VERSION) || \
|
||||
GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION)
|
||||
|
||||
ENVTEST = $(shell pwd)/bin/setup-envtest
|
||||
.PHONY: envtest
|
||||
envtest: ## Download envtest-setup locally if necessary.
|
||||
$(call go-get-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest@latest)
|
||||
envtest: $(ENVTEST) ## Download envtest-setup locally if necessary.
|
||||
$(ENVTEST): $(LOCALBIN)
|
||||
test -s $(LOCALBIN)/setup-envtest || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest
|
||||
|
||||
# go-get-tool will 'go install' any package $2 and install it to $1.
|
||||
PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))
|
||||
define go-get-tool
|
||||
@[ -f $(1) ] || { \
|
||||
set -e ;\
|
||||
GOBIN=$(PROJECT_DIR)/bin go install $(2) ;\
|
||||
}
|
||||
endef
|
||||
|
|
6
PROJECT
6
PROJECT
|
@ -1,6 +1,10 @@
|
|||
# Code generated by tool. DO NOT EDIT.
|
||||
# This file is used to track the info used to scaffold your project
|
||||
# and allow the plugins properly work.
|
||||
# More info: https://book.kubebuilder.io/reference/project-config.html
|
||||
domain: spiffe.io
|
||||
layout:
|
||||
- go.kubebuilder.io/v3
|
||||
- go.kubebuilder.io/v4
|
||||
projectName: spire-controller-manager
|
||||
repo: github.com/spiffe/spire-controller-manager
|
||||
resources:
|
||||
|
|
15
README.md
15
README.md
|
@ -87,12 +87,13 @@ verify that [manager-role](/config/rbac/role.yaml) is up-to-date.
|
|||
The SPIRE APIs used by the SPIRE Controller Manager are generally stable and
|
||||
supported since at least SPIRE v1.0. However, the API has gained support for
|
||||
additional entry fields beyond what was supported in SPIRE v1.0. Notably, these
|
||||
include both the `jwt_svid_ttl` and the `hint` fields. The ClusterStaticEntry
|
||||
CRD allows these fields to be set, however, a SPIRE server that does not
|
||||
support these fields will not retain them. This means if these fields are set
|
||||
on a ClusterStaticEntry with an older version of SPIRE, the SPIRE Controller
|
||||
Manager will continously try to reconcile SPIRE server. In order to use these
|
||||
fields, you must be on a version of SPIRE Server which supports them.
|
||||
include the `jwt_svid_ttl`, `hint` and the `store_svid` fields. The
|
||||
ClusterStaticEntry CRD allows these fields to be set, however, a SPIRE server
|
||||
that does not support these fields will not retain them. This means if these
|
||||
fields are set on a ClusterStaticEntry with an older version of SPIRE, the
|
||||
SPIRE Controller Manager will continously try to reconcile SPIRE server. In
|
||||
order to use these fields, you must be on a version of SPIRE Server which
|
||||
supports them.
|
||||
|
||||
At the moment, SPIRE Controller Manager will silently try and reconcile these
|
||||
fields over and over. Future updates may cause the SPIRE Controller Manager
|
||||
|
@ -103,6 +104,8 @@ The `hint` field is supported as of SPIRE 1.6.3.
|
|||
|
||||
The `jwt_svid_ttl` field is supported as of SPIRE 1.5.0.
|
||||
|
||||
The `store_svid` field is supported as of SPIRE 1.1.0.
|
||||
|
||||
## Demo
|
||||
|
||||
[Link](demo)
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
package v1alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
)
|
||||
|
||||
func loadClusterFederatedTrustDomainFile(path string, scheme *runtime.Scheme, expandEnv bool) (*ClusterFederatedTrustDomain, error) {
|
||||
var entry ClusterFederatedTrustDomain
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read file at %s: %w", path, err)
|
||||
}
|
||||
|
||||
if expandEnv {
|
||||
content = []byte(os.ExpandEnv(string(content)))
|
||||
}
|
||||
|
||||
codecs := serializer.NewCodecFactory(scheme)
|
||||
|
||||
// Regardless of if the bytes are of any external version,
|
||||
// it will be read successfully and converted into the internal version
|
||||
if err = runtime.DecodeInto(codecs.UniversalDecoder(), content, &entry); err != nil {
|
||||
return nil, fmt.Errorf("could not decode file (%s) into runtime.Object: %w", path, err)
|
||||
}
|
||||
|
||||
return &entry, nil
|
||||
}
|
||||
|
||||
func ListClusterFederatedTrustDomains(_ context.Context, manifestPath string) ([]ClusterFederatedTrustDomain, error) {
|
||||
scheme := runtime.NewScheme()
|
||||
res := make([]ClusterFederatedTrustDomain, 0)
|
||||
expandEnv := false
|
||||
files, err := os.ReadDir(manifestPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, file := range files {
|
||||
if !strings.HasSuffix(file.Name(), ".yaml") {
|
||||
continue
|
||||
}
|
||||
fullfile := path.Join(manifestPath, file.Name())
|
||||
entry, err := loadClusterFederatedTrustDomainFile(fullfile, scheme, expandEnv)
|
||||
// Ignore files of the wrong type in manifestPath
|
||||
if entry.APIVersion != "spire.spiffe.io/v1alpha1" || entry.Kind != "ClusterFederatedTrustDomain" {
|
||||
continue
|
||||
}
|
||||
// Right file type, but error loading
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = append(res, *entry)
|
||||
}
|
||||
return res, nil
|
||||
}
|
|
@ -37,6 +37,10 @@ type ClusterFederatedTrustDomainSpec struct {
|
|||
// domain. This field is optional when the resource is created.
|
||||
// +kubebuilder:validation:Optional
|
||||
TrustDomainBundle string `json:"trustDomainBundle,omitempty"`
|
||||
|
||||
// Set which Controller Class will act on this object
|
||||
// +kubebuilder:validation:Optional
|
||||
ClassName string `json:"className,omitempty"`
|
||||
}
|
||||
|
||||
// BundleEndpointProfile is the profile for the federated trust domain
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package v1alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
|
@ -36,6 +37,7 @@ var clusterfederatedtrustdomainlog = logf.Log.WithName("clusterfederatedtrustdom
|
|||
func (r *ClusterFederatedTrustDomain) SetupWebhookWithManager(mgr ctrl.Manager) error {
|
||||
return ctrl.NewWebhookManagedBy(mgr).
|
||||
For(r).
|
||||
WithValidator(&ClusterFederatedTrustDomainCustomValidator{}).
|
||||
Complete()
|
||||
}
|
||||
|
||||
|
@ -44,28 +46,40 @@ func (r *ClusterFederatedTrustDomain) SetupWebhookWithManager(mgr ctrl.Manager)
|
|||
// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation.
|
||||
//+kubebuilder:webhook:path=/validate-spire-spiffe-io-v1alpha1-clusterfederatedtrustdomain,mutating=false,failurePolicy=fail,sideEffects=None,groups=spire.spiffe.io,resources=clusterfederatedtrustdomains,verbs=create;update,versions=v1alpha1,name=vclusterfederatedtrustdomain.kb.io,admissionReviewVersions=v1
|
||||
|
||||
var _ webhook.Validator = &ClusterFederatedTrustDomain{}
|
||||
|
||||
// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
|
||||
func (r *ClusterFederatedTrustDomain) ValidateCreate() (admission.Warnings, error) {
|
||||
clusterfederatedtrustdomainlog.Info("validate create", "name", r.Name)
|
||||
return r.validate()
|
||||
type ClusterFederatedTrustDomainCustomValidator struct {
|
||||
// TODO(user): Add more fields as needed for validation
|
||||
}
|
||||
|
||||
// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
|
||||
func (r *ClusterFederatedTrustDomain) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
|
||||
clusterfederatedtrustdomainlog.Info("validate update", "name", r.Name)
|
||||
return r.validate()
|
||||
var _ webhook.CustomValidator = &ClusterFederatedTrustDomainCustomValidator{}
|
||||
|
||||
// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type
|
||||
func (r *ClusterFederatedTrustDomainCustomValidator) ValidateCreate(_ context.Context, obj runtime.Object) (admission.Warnings, error) {
|
||||
o, ok := obj.(*ClusterFederatedTrustDomain)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected a ClusterFederatedTrustDomain object but got %T", obj)
|
||||
}
|
||||
clusterfederatedtrustdomainlog.Info("validate create", "name", o.Name)
|
||||
return r.validate(o)
|
||||
}
|
||||
|
||||
// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
|
||||
func (r *ClusterFederatedTrustDomain) ValidateDelete() (admission.Warnings, error) {
|
||||
// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type
|
||||
func (r *ClusterFederatedTrustDomainCustomValidator) ValidateUpdate(_ context.Context, _ runtime.Object, nobj runtime.Object) (admission.Warnings, error) {
|
||||
o, ok := nobj.(*ClusterFederatedTrustDomain)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected a ClusterFederatedTrustDomain object but got %T", nobj)
|
||||
}
|
||||
clusterfederatedtrustdomainlog.Info("validate update", "name", o.Name)
|
||||
return r.validate(o)
|
||||
}
|
||||
|
||||
// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type
|
||||
func (r *ClusterFederatedTrustDomainCustomValidator) ValidateDelete(context.Context, runtime.Object) (admission.Warnings, error) {
|
||||
// Deletes are not validated.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *ClusterFederatedTrustDomain) validate() (admission.Warnings, error) {
|
||||
_, err := ParseClusterFederatedTrustDomainSpec(&r.Spec)
|
||||
func (r *ClusterFederatedTrustDomainCustomValidator) validate(o *ClusterFederatedTrustDomain) (admission.Warnings, error) {
|
||||
_, err := ParseClusterFederatedTrustDomainSpec(&o.Spec)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
|
@ -20,9 +20,6 @@ import (
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
|
||||
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
|
||||
|
||||
// ClusterSPIFFEIDSpec defines the desired state of ClusterSPIFFEID
|
||||
type ClusterSPIFFEIDSpec struct {
|
||||
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||
|
@ -74,13 +71,25 @@ type ClusterSPIFFEIDSpec struct {
|
|||
|
||||
// Downstream indicates that the entry describes a downstream SPIRE server.
|
||||
Downstream bool `json:"downstream,omitempty"`
|
||||
|
||||
// AutoPopulateDNSNames indicates whether or not to auto populate service DNS names.
|
||||
AutoPopulateDNSNames bool `json:"autoPopulateDNSNames,omitempty"`
|
||||
|
||||
// Set which Controller Class will act on this object
|
||||
// +kubebuilder:validation:Optional
|
||||
ClassName string `json:"className,omitempty"`
|
||||
|
||||
// Apply this ID only if there are no other matching non fallback ClusterSPIFFEIDs.
|
||||
// +kubebuilder:validation:Optional
|
||||
Fallback bool `json:"fallback,omitempty"`
|
||||
|
||||
// Set the entry hint
|
||||
// +kubebuilder:validation:Optional
|
||||
Hint string `json:"hint,omitempty"`
|
||||
}
|
||||
|
||||
// ClusterSPIFFEIDStatus defines the observed state of ClusterSPIFFEID
|
||||
type ClusterSPIFFEIDStatus struct {
|
||||
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||
// Important: Run "make" to regenerate code after modifying this file
|
||||
|
||||
// Stats produced by the last entry reconciliation run
|
||||
// +kubebuilder:validation:Optional
|
||||
Stats ClusterSPIFFEIDStats `json:"stats"`
|
||||
|
@ -135,7 +144,8 @@ type ClusterSPIFFEID struct {
|
|||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec ClusterSPIFFEIDSpec `json:"spec,omitempty"`
|
||||
Spec ClusterSPIFFEIDSpec `json:"spec,omitempty"`
|
||||
// +optional
|
||||
Status ClusterSPIFFEIDStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package v1alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"text/template"
|
||||
|
@ -44,6 +45,7 @@ var clusterspiffeidlog = logf.Log.WithName("clusterspiffeid-resource")
|
|||
func (r *ClusterSPIFFEID) SetupWebhookWithManager(mgr ctrl.Manager) error {
|
||||
return ctrl.NewWebhookManagedBy(mgr).
|
||||
For(r).
|
||||
WithValidator(&ClusterSPIFFEIDCustomValidator{}).
|
||||
Complete()
|
||||
}
|
||||
|
||||
|
@ -52,30 +54,42 @@ func (r *ClusterSPIFFEID) SetupWebhookWithManager(mgr ctrl.Manager) error {
|
|||
// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation.
|
||||
//+kubebuilder:webhook:path=/validate-spire-spiffe-io-v1alpha1-clusterspiffeid,mutating=false,failurePolicy=fail,sideEffects=None,groups=spire.spiffe.io,resources=clusterspiffeids,verbs=create;update,versions=v1alpha1,name=vclusterspiffeid.kb.io,admissionReviewVersions=v1
|
||||
|
||||
var _ webhook.Validator = &ClusterSPIFFEID{}
|
||||
|
||||
// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
|
||||
func (r *ClusterSPIFFEID) ValidateCreate() (admission.Warnings, error) {
|
||||
clusterspiffeidlog.Info("validate create", "name", r.Name)
|
||||
|
||||
return r.validate()
|
||||
type ClusterSPIFFEIDCustomValidator struct {
|
||||
// TODO(user): Add more fields as needed for validation
|
||||
}
|
||||
|
||||
// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
|
||||
func (r *ClusterSPIFFEID) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
|
||||
clusterspiffeidlog.Info("validate update", "name", r.Name)
|
||||
var _ webhook.CustomValidator = &ClusterSPIFFEIDCustomValidator{}
|
||||
|
||||
return r.validate()
|
||||
// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type
|
||||
func (r *ClusterSPIFFEIDCustomValidator) ValidateCreate(_ context.Context, obj runtime.Object) (admission.Warnings, error) {
|
||||
o, ok := obj.(*ClusterSPIFFEID)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected a ClusterSPIFFEID object but got %T", obj)
|
||||
}
|
||||
clusterspiffeidlog.Info("validate create", "name", o.Name)
|
||||
|
||||
return r.validate(o)
|
||||
}
|
||||
|
||||
// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
|
||||
func (r *ClusterSPIFFEID) ValidateDelete() (admission.Warnings, error) {
|
||||
// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type
|
||||
func (r *ClusterSPIFFEIDCustomValidator) ValidateUpdate(_ context.Context, _ runtime.Object, nobj runtime.Object) (admission.Warnings, error) {
|
||||
o, ok := nobj.(*ClusterSPIFFEID)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected a ClusterSPIFFEID object but got %T", nobj)
|
||||
}
|
||||
clusterspiffeidlog.Info("validate update", "name", o.Name)
|
||||
|
||||
return r.validate(o)
|
||||
}
|
||||
|
||||
// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type
|
||||
func (r *ClusterSPIFFEIDCustomValidator) ValidateDelete(context.Context, runtime.Object) (admission.Warnings, error) {
|
||||
// Deletes are not validated.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *ClusterSPIFFEID) validate() (admission.Warnings, error) {
|
||||
_, err := ParseClusterSPIFFEIDSpec(&r.Spec)
|
||||
func (r *ClusterSPIFFEIDCustomValidator) validate(o *ClusterSPIFFEID) (admission.Warnings, error) {
|
||||
_, err := ParseClusterSPIFFEIDSpec(&o.Spec)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -92,6 +106,8 @@ type ParsedClusterSPIFFEIDSpec struct {
|
|||
WorkloadSelectorTemplates []*template.Template
|
||||
Admin bool
|
||||
Downstream bool
|
||||
AutoPopulateDNSNames bool
|
||||
Hint string
|
||||
}
|
||||
|
||||
// ParseClusterSPIFFEIDSpec parses and validates the fields in the ClusterSPIFFEIDSpec
|
||||
|
@ -159,5 +175,7 @@ func ParseClusterSPIFFEIDSpec(spec *ClusterSPIFFEIDSpec) (*ParsedClusterSPIFFEID
|
|||
WorkloadSelectorTemplates: workloadSelectorTemplates,
|
||||
Admin: spec.Admin,
|
||||
Downstream: spec.Downstream,
|
||||
AutoPopulateDNSNames: spec.AutoPopulateDNSNames,
|
||||
Hint: spec.Hint,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
package v1alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
)
|
||||
|
||||
func loadClusterStaticEntryFile(path string, scheme *runtime.Scheme, expandEnv bool) (*ClusterStaticEntry, error) {
|
||||
var entry ClusterStaticEntry
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read file at %s: %w", path, err)
|
||||
}
|
||||
|
||||
if expandEnv {
|
||||
content = []byte(os.ExpandEnv(string(content)))
|
||||
}
|
||||
|
||||
codecs := serializer.NewCodecFactory(scheme)
|
||||
|
||||
// Regardless of if the bytes are of any external version,
|
||||
// it will be read successfully and converted into the internal version
|
||||
if err = runtime.DecodeInto(codecs.UniversalDecoder(), content, &entry); err != nil {
|
||||
return nil, fmt.Errorf("could not decode file (%s) into runtime.Object: %w", path, err)
|
||||
}
|
||||
|
||||
return &entry, nil
|
||||
}
|
||||
|
||||
func ListClusterStaticEntries(_ context.Context, manifestPath string) ([]ClusterStaticEntry, error) {
|
||||
scheme := runtime.NewScheme()
|
||||
res := make([]ClusterStaticEntry, 0)
|
||||
expandEnv := false
|
||||
files, err := os.ReadDir(manifestPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, file := range files {
|
||||
if !strings.HasSuffix(file.Name(), ".yaml") {
|
||||
continue
|
||||
}
|
||||
fullfile := path.Join(manifestPath, file.Name())
|
||||
entry, err := loadClusterStaticEntryFile(fullfile, scheme, expandEnv)
|
||||
// Ignore files of the wrong type in manifestPath
|
||||
if entry.APIVersion != "spire.spiffe.io/v1alpha1" || entry.Kind != "ClusterStaticEntry" {
|
||||
continue
|
||||
}
|
||||
// Right file type, but error loading
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = append(res, *entry)
|
||||
}
|
||||
return res, nil
|
||||
}
|
|
@ -35,6 +35,10 @@ type ClusterStaticEntrySpec struct {
|
|||
Hint string `json:"hint,omitempty"`
|
||||
Admin bool `json:"admin,omitempty"`
|
||||
Downstream bool `json:"downstream,omitempty"`
|
||||
StoreSVID bool `json:"storeSVID,omitempty"`
|
||||
// Set which Controller Class will act on this object
|
||||
// +kubebuilder:validation:Optional
|
||||
ClassName string `json:"className,omitempty"`
|
||||
}
|
||||
|
||||
// ClusterStaticEntryStatus defines the observed state of ClusterStaticEntry
|
||||
|
|
|
@ -1,32 +1,38 @@
|
|||
package v1alpha1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/cache"
|
||||
)
|
||||
|
||||
func LoadOptionsFromFile(path string, scheme *runtime.Scheme, options *ctrl.Options, config *ControllerManagerConfig) error {
|
||||
if err := loadFile(path, scheme, config); err != nil {
|
||||
func LoadOptionsFromFile(path string, scheme *runtime.Scheme, options *ctrl.Options, config *ControllerManagerConfig, expandEnv bool) error {
|
||||
if err := loadFile(path, scheme, config, expandEnv); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
addOptionsFromConfigSpec(options, config.ControllerManagerConfigurationSpec)
|
||||
|
||||
return nil
|
||||
return addOptionsFromConfigSpec(options, config.ControllerManagerConfigurationSpec)
|
||||
}
|
||||
|
||||
func loadFile(path string, scheme *runtime.Scheme, config *ControllerManagerConfig) error {
|
||||
func loadFile(path string, scheme *runtime.Scheme, config *ControllerManagerConfig, expandEnv bool) error {
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read file at %s: %w", path, err)
|
||||
}
|
||||
|
||||
if expandEnv {
|
||||
content = []byte(os.ExpandEnv(string(content)))
|
||||
}
|
||||
|
||||
codecs := serializer.NewCodecFactory(scheme)
|
||||
|
||||
// Regardless of if the bytes are of any external version,
|
||||
|
@ -38,19 +44,42 @@ func loadFile(path string, scheme *runtime.Scheme, config *ControllerManagerConf
|
|||
return nil
|
||||
}
|
||||
|
||||
func addOptionsFromConfigSpec(o *ctrl.Options, configSpec ControllerManagerConfigurationSpec) {
|
||||
func addOptionsFromConfigSpec(o *ctrl.Options, configSpec ControllerManagerConfigurationSpec) error {
|
||||
setLeaderElectionConfig(o, configSpec)
|
||||
|
||||
if o.Cache.SyncPeriod == nil && configSpec.SyncPeriod != nil {
|
||||
o.Cache.SyncPeriod = &configSpec.SyncPeriod.Duration
|
||||
}
|
||||
|
||||
if len(o.Cache.Namespaces) == 0 && configSpec.CacheNamespace != "" {
|
||||
o.Cache.Namespaces = []string{configSpec.CacheNamespace}
|
||||
if len(o.Cache.DefaultNamespaces) == 0 {
|
||||
switch {
|
||||
case configSpec.CacheNamespace != "" && len(configSpec.CacheNamespaces) > 0:
|
||||
return errors.New("cacheNamespace or cacheNamespaces can be used, but not both")
|
||||
case configSpec.CacheNamespace != "":
|
||||
o.Cache.DefaultNamespaces = map[string]cache.Config{
|
||||
configSpec.CacheNamespace: {},
|
||||
}
|
||||
|
||||
case len(configSpec.CacheNamespaces) > 0:
|
||||
o.Cache.DefaultNamespaces = make(map[string]cache.Config, len(configSpec.CacheNamespaces))
|
||||
for namespace, opts := range configSpec.CacheNamespaces {
|
||||
cacheConfig := cache.Config{}
|
||||
if opts != nil {
|
||||
if len(opts.LabelSelectors) > 0 {
|
||||
cacheConfig.LabelSelector = labels.SelectorFromSet(opts.LabelSelectors)
|
||||
}
|
||||
if len(opts.FieldSelectors) > 0 {
|
||||
cacheConfig.FieldSelector = fields.SelectorFromSet(opts.FieldSelectors)
|
||||
}
|
||||
}
|
||||
|
||||
o.Cache.DefaultNamespaces[namespace] = cacheConfig
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if o.MetricsBindAddress == "" && configSpec.Metrics.BindAddress != "" {
|
||||
o.MetricsBindAddress = configSpec.Metrics.BindAddress
|
||||
if o.Metrics.BindAddress == "" && configSpec.Metrics.BindAddress != "" {
|
||||
o.Metrics.BindAddress = configSpec.Metrics.BindAddress
|
||||
}
|
||||
|
||||
if o.HealthProbeBindAddress == "" && configSpec.Health.HealthProbeBindAddress != "" {
|
||||
|
@ -74,6 +103,8 @@ func addOptionsFromConfigSpec(o *ctrl.Options, configSpec ControllerManagerConfi
|
|||
o.Controller.GroupKindConcurrency = configSpec.Controller.GroupKindConcurrency
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setLeaderElectionConfig(o *ctrl.Options, obj ControllerManagerConfigurationSpec) {
|
||||
|
|
|
@ -10,11 +10,14 @@ import (
|
|||
spirev1alpha1 "github.com/spiffe/spire-controller-manager/api/v1alpha1"
|
||||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/component-base/config/v1alpha1"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/cache"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -37,6 +40,32 @@ ignoreNamespaces:
|
|||
- spire-system
|
||||
- local-path-storage
|
||||
`
|
||||
|
||||
fileContentExpandEnv = `
|
||||
apiVersion: spire.spiffe.io/v1alpha1
|
||||
kind: ControllerManagerConfig
|
||||
clusterName: cluster2
|
||||
trustDomain: $TRUST_DOMAIN
|
||||
`
|
||||
|
||||
cacheNamespace = `
|
||||
cacheNamespace: default
|
||||
`
|
||||
cacheNamespaces = `
|
||||
cacheNamespaces:
|
||||
default:
|
||||
nsWithLabel:
|
||||
labelSelectors:
|
||||
lName: l1
|
||||
nsWithField:
|
||||
fieldSelectors:
|
||||
fName: f1
|
||||
nsWithBoth:
|
||||
labelSelectors:
|
||||
lName: l1
|
||||
fieldSelectors:
|
||||
fName: f1
|
||||
`
|
||||
)
|
||||
|
||||
func TestLoadOptionsFromFileReplaceDefaultValues(t *testing.T) {
|
||||
|
@ -56,7 +85,7 @@ func TestLoadOptionsFromFileReplaceDefaultValues(t *testing.T) {
|
|||
ValidatingWebhookConfigurationName: "foo-webhook",
|
||||
}
|
||||
|
||||
err := spirev1alpha1.LoadOptionsFromFile(path, scheme, &options, &ctrlConfig)
|
||||
err := spirev1alpha1.LoadOptionsFromFile(path, scheme, &options, &ctrlConfig, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
ok := true
|
||||
|
@ -94,7 +123,7 @@ func TestLoadOptionsFromFileReplaceDefaultValues(t *testing.T) {
|
|||
require.Equal(t, "spire-system", options.LeaderElectionNamespace)
|
||||
require.True(t, true, options.LeaderElection)
|
||||
require.Equal(t, "98c9c988.spiffe.io", options.LeaderElectionID)
|
||||
require.Equal(t, "127.0.0.1:8082", options.MetricsBindAddress)
|
||||
require.Equal(t, "127.0.0.1:8082", options.Metrics.BindAddress)
|
||||
}
|
||||
|
||||
func TestLoadOptionsFromFileInvalidPath(t *testing.T) {
|
||||
|
@ -107,10 +136,124 @@ func TestLoadOptionsFromFileInvalidPath(t *testing.T) {
|
|||
ValidatingWebhookConfigurationName: "foo-webhook",
|
||||
}
|
||||
|
||||
err := spirev1alpha1.LoadOptionsFromFile("", scheme, &options, &ctrlConfig)
|
||||
err := spirev1alpha1.LoadOptionsFromFile("", scheme, &options, &ctrlConfig, false)
|
||||
require.EqualError(t, err, "could not read file at : open : no such file or directory")
|
||||
|
||||
err = spirev1alpha1.LoadOptionsFromFile("foo.yaml", scheme, &options, &ctrlConfig)
|
||||
err = spirev1alpha1.LoadOptionsFromFile("foo.yaml", scheme, &options, &ctrlConfig, false)
|
||||
fmt.Printf("err :%v\n", err)
|
||||
require.EqualError(t, err, "could not read file at foo.yaml: open foo.yaml: no such file or directory")
|
||||
}
|
||||
|
||||
func TestLoadOptionsFromFileExpandEnv(t *testing.T) {
|
||||
t.Setenv("TRUST_DOMAIN", "example.org")
|
||||
|
||||
tempDir := t.TempDir()
|
||||
path := filepath.Join(tempDir, "config.yaml")
|
||||
require.NoError(t, os.WriteFile(path, []byte(fileContentExpandEnv), 0600))
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
options := ctrl.Options{Scheme: scheme}
|
||||
|
||||
ctrlConfig := spirev1alpha1.ControllerManagerConfig{}
|
||||
|
||||
tests := []struct {
|
||||
expandEnv bool
|
||||
expectedValue string
|
||||
}{
|
||||
{
|
||||
expandEnv: true,
|
||||
expectedValue: "example.org",
|
||||
},
|
||||
{
|
||||
expandEnv: false,
|
||||
expectedValue: "$TRUST_DOMAIN",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
err := spirev1alpha1.LoadOptionsFromFile(path, scheme, &options, &ctrlConfig, test.expandEnv)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.expectedValue, ctrlConfig.TrustDomain)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadOptionsWithCacheNamespaces(t *testing.T) {
|
||||
scheme := runtime.NewScheme()
|
||||
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
|
||||
utilruntime.Must(spirev1alpha1.AddToScheme(scheme))
|
||||
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
cacheNamespace string
|
||||
expectErr string
|
||||
expectNamespaces map[string]cache.Config
|
||||
}{
|
||||
{
|
||||
name: "no namespace",
|
||||
expectNamespaces: nil,
|
||||
},
|
||||
{
|
||||
name: "using namespaces",
|
||||
cacheNamespace: cacheNamespace,
|
||||
expectNamespaces: map[string]cache.Config{
|
||||
"default": {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with cacheNamespaces",
|
||||
cacheNamespace: cacheNamespaces,
|
||||
expectNamespaces: map[string]cache.Config{
|
||||
"default": {},
|
||||
"nsWithLabel": {
|
||||
LabelSelector: labels.SelectorFromSet(labels.Set{
|
||||
"lName": "l1",
|
||||
}),
|
||||
},
|
||||
"nsWithField": {
|
||||
FieldSelector: fields.SelectorFromSet(fields.Set{
|
||||
"fName": "f1",
|
||||
}),
|
||||
},
|
||||
"nsWithBoth": {
|
||||
LabelSelector: labels.SelectorFromSet(labels.Set{
|
||||
"lName": "l1",
|
||||
}),
|
||||
FieldSelector: fields.SelectorFromSet(fields.Set{
|
||||
"fName": "f1",
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with cacheNamespace and cacheNamespaces",
|
||||
cacheNamespace: cacheNamespace + cacheNamespaces,
|
||||
expectErr: "cacheNamespace or cacheNamespaces can be used, but not both",
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
path := filepath.Join(tempDir, "config.yaml")
|
||||
|
||||
config := fileContent + tt.cacheNamespace
|
||||
require.NoError(t, os.WriteFile(path, []byte(config), 0600))
|
||||
|
||||
options := ctrl.Options{Scheme: scheme}
|
||||
|
||||
ctrlConfig := spirev1alpha1.ControllerManagerConfig{
|
||||
IgnoreNamespaces: []string{"kube-system", "kube-public", "spire-system", "foo"},
|
||||
GCInterval: time.Minute,
|
||||
ValidatingWebhookConfigurationName: "foo-webhook",
|
||||
}
|
||||
|
||||
err := spirev1alpha1.LoadOptionsFromFile(path, scheme, &options, &ctrlConfig, false)
|
||||
if tt.expectErr != "" {
|
||||
require.EqualError(t, err, tt.expectErr)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, tt.expectNamespaces, options.Cache.DefaultNamespaces)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,6 +56,12 @@ type ControllerManagerConfig struct {
|
|||
|
||||
// SPIREServerSocketPath is the path to the SPIRE Server API socket
|
||||
SPIREServerSocketPath string `json:"spireServerSocketPath"`
|
||||
|
||||
// LogLevel is the log level for the controller manager
|
||||
LogLevel string `json:"logLevel"`
|
||||
|
||||
// LogEncoding is the log encoding for the controller manager
|
||||
LogEncoding string `json:"logEncoding"`
|
||||
}
|
||||
|
||||
// ControllerManagerConfigurationSpec defines the desired state of GenericControllerManagerConfiguration.
|
||||
|
@ -70,12 +76,13 @@ type ControllerManagerConfigurationSpec struct {
|
|||
SyncPeriod *metav1.Duration `json:"syncPeriod,omitempty"`
|
||||
|
||||
// LeaderElection is the LeaderElection config to be used when configuring
|
||||
// the manager.Manager leader election
|
||||
// the manager.Manager leader election.
|
||||
// +optional
|
||||
LeaderElection *configv1alpha1.LeaderElectionConfiguration `json:"leaderElection,omitempty"`
|
||||
|
||||
// CacheNamespace if specified restricts the manager's cache to watch objects in
|
||||
// the desired namespace Defaults to all namespaces
|
||||
// the desired namespace. Defaults to all namespaces.
|
||||
// Deprecated: use cacheNamespaces instead
|
||||
//
|
||||
// Note: If a namespace is specified, controllers can still Watch for a
|
||||
// cluster-scoped resource (e.g Node). For namespaced resources the cache
|
||||
|
@ -83,6 +90,11 @@ type ControllerManagerConfigurationSpec struct {
|
|||
// +optional
|
||||
CacheNamespace string `json:"cacheNamespace,omitempty"`
|
||||
|
||||
// CacheNamespaces if specified restricts the manager's cache to watch objects in
|
||||
// the desired namespaces. Defaults to all namespaces.
|
||||
// +optional
|
||||
CacheNamespaces map[string]*NamespaceConfig `json:"cacheNamespaces,omitempty"`
|
||||
|
||||
// GracefulShutdownTimeout is the duration given to runnable to stop before the manager actually returns on stop.
|
||||
// To disable graceful shutdown, set to time.Duration(0)
|
||||
// To use graceful shutdown without timeout, set to a negative duration, e.G. time.Duration(-1)
|
||||
|
@ -105,6 +117,63 @@ type ControllerManagerConfigurationSpec struct {
|
|||
// Webhook contains the controllers webhook configuration
|
||||
// +optional
|
||||
Webhook ControllerWebhook `json:"webhook,omitempty"`
|
||||
|
||||
// ClassName contains the name of a class to watch CRs for. Others will be ignored.
|
||||
// If unset all will be watched.
|
||||
// +optional
|
||||
ClassName string `json:"className,omitempty"`
|
||||
|
||||
// If WatchClassless is set and ClassName is set, any CR without a ClassName
|
||||
// specified will also be handled by this controller.
|
||||
// +optional
|
||||
WatchClassless bool `json:"watchClassless,omitempty"`
|
||||
|
||||
// If specified, uses a different parent id template for linking pods to nodes
|
||||
// +optional
|
||||
ParentIDTemplate string `json:"parentIDTemplate,omitempty"`
|
||||
|
||||
// If specified, only syncs the specified CR types. Defaults to all.
|
||||
// +optional
|
||||
Reconcile *ReconcileConfig `json:"reconcile,omitempty"`
|
||||
|
||||
// If specified, prefixes each entry id with `<prefix>.`. Entries without the Prefix will be ignored (except ones marked for cleanup, see EntryIDPrefixCleanup).
|
||||
// +optiional
|
||||
EntryIDPrefix string `json:"entryIDPrefix,omitempty"`
|
||||
|
||||
// If specified, entries with the specified prefix will be removed. If set to "" it will clean up all unprefixed entries.
|
||||
// It can not be set to the same value as EntryIDPrefix.
|
||||
// Generally useful when switching from nonprefixed to prefixed, or between two different prefixes.
|
||||
// +optiional
|
||||
EntryIDPrefixCleanup *string `json:"entryIDPrefixCleanup,omitempty"`
|
||||
|
||||
// When configured, read yaml objects from the specified path rather then from Kubernetes.
|
||||
StaticManifestPath *string `json:"staticManifestPath,omitempty"`
|
||||
}
|
||||
|
||||
// ReconcileConfig configuration used to enable/disable syncing various types
|
||||
type ReconcileConfig struct {
|
||||
// ClusterSpiffeIds enable syncing of clusterspiffeids
|
||||
// +optional
|
||||
ClusterSPIFFEIDs bool `json:"clusterSPIFFEIDs,omitempty"`
|
||||
|
||||
// ClusterFederatedTrustDomains enable syncing of clusterfederatedtrustdomains
|
||||
// +optional
|
||||
ClusterFederatedTrustDomains bool `json:"clusterFederatedTrustDomains,omitempty"`
|
||||
|
||||
// ClusterStaticEntries enable syncing of clusterstaticentries
|
||||
// +optional
|
||||
ClusterStaticEntries bool `json:"clusterStaticEntries,omitempty"`
|
||||
}
|
||||
|
||||
// NamespaceConfig configuration used to filter cached namespaces
|
||||
type NamespaceConfig struct {
|
||||
// LabelSelectors map of Labels selectors
|
||||
// +optional
|
||||
LabelSelectors map[string]string `json:"labelSelectors,omitempty"`
|
||||
|
||||
// FieldSelectors map of Fields selectors
|
||||
// +optional
|
||||
FieldSelectors map[string]string `json:"fieldSelectors,omitempty"`
|
||||
}
|
||||
|
||||
// ControllerConfigurationSpec defines the global configuration for
|
||||
|
|
|
@ -22,25 +22,30 @@ import (
|
|||
"fmt"
|
||||
"net"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
. "github.com/onsi/ginkgo/v2" //nolint:revive // auto-generated
|
||||
. "github.com/onsi/gomega" //nolint:revive // auto-generated
|
||||
|
||||
admissionv1beta1 "k8s.io/api/admission/v1beta1"
|
||||
admissionv1 "k8s.io/api/admission/v1"
|
||||
//+kubebuilder:scaffold:imports
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
apimachineryruntime "k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/rest"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||
)
|
||||
|
||||
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
|
||||
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
|
||||
|
||||
var cfg *rest.Config
|
||||
var k8sClient client.Client
|
||||
var testEnv *envtest.Environment
|
||||
var ctx context.Context
|
||||
|
@ -61,23 +66,31 @@ var _ = BeforeSuite(func() {
|
|||
testEnv = &envtest.Environment{
|
||||
CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")},
|
||||
ErrorIfCRDPathMissing: false,
|
||||
|
||||
// The BinaryAssetsDirectory is only required if you want to run the tests directly
|
||||
// without call the makefile target test. If not informed it will look for the
|
||||
// default path defined in controller-runtime which is /usr/local/kubebuilder/.
|
||||
// Note that you must have the required binaries setup under the bin directory to perform
|
||||
// the tests directly. When we run make test it will be setup and used automatically.
|
||||
BinaryAssetsDirectory: filepath.Join("..", "..", "bin", "k8s",
|
||||
fmt.Sprintf("1.28.0-%s-%s", runtime.GOOS, runtime.GOARCH)),
|
||||
|
||||
WebhookInstallOptions: envtest.WebhookInstallOptions{
|
||||
Paths: []string{filepath.Join("..", "..", "config", "webhook")},
|
||||
},
|
||||
}
|
||||
|
||||
cfg, err := testEnv.Start()
|
||||
var err error
|
||||
// cfg is defined in this file globally.
|
||||
cfg, err = testEnv.Start()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(cfg).NotTo(BeNil())
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
scheme := apimachineryruntime.NewScheme()
|
||||
err = AddToScheme(scheme)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = admissionv1beta1.AddToScheme(scheme)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = admissionv1beta1.AddToScheme(scheme)
|
||||
err = admissionv1.AddToScheme(scheme)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
//+kubebuilder:scaffold:scheme
|
||||
|
@ -89,13 +102,16 @@ var _ = BeforeSuite(func() {
|
|||
// start webhook server using Manager
|
||||
webhookInstallOptions := &testEnv.WebhookInstallOptions
|
||||
mgr, err := ctrl.NewManager(cfg, ctrl.Options{
|
||||
Scheme: scheme,
|
||||
Host: webhookInstallOptions.LocalServingHost,
|
||||
Port: webhookInstallOptions.LocalServingPort,
|
||||
CertDir: webhookInstallOptions.LocalServingCertDir,
|
||||
LeaderElection: false,
|
||||
MetricsBindAddress: "0",
|
||||
Scheme: scheme,
|
||||
WebhookServer: webhook.NewServer(webhook.Options{
|
||||
Host: webhookInstallOptions.LocalServingHost,
|
||||
Port: webhookInstallOptions.LocalServingPort,
|
||||
CertDir: webhookInstallOptions.LocalServingCertDir,
|
||||
}),
|
||||
LeaderElection: false,
|
||||
Metrics: metricsserver.Options{BindAddress: "0"},
|
||||
})
|
||||
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = (&ClusterFederatedTrustDomain{}).SetupWebhookWithManager(mgr)
|
||||
|
@ -116,7 +132,7 @@ var _ = BeforeSuite(func() {
|
|||
dialer := &net.Dialer{Timeout: time.Second}
|
||||
addrPort := fmt.Sprintf("%s:%d", webhookInstallOptions.LocalServingHost, webhookInstallOptions.LocalServingPort)
|
||||
Eventually(func() error {
|
||||
conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true}) // nolint: gosec // this is intentional for the unit test
|
||||
conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true}) //nolint: gosec // this is intentional for the unit test
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright 2023 SPIRE Authors.
|
||||
|
@ -70,6 +69,21 @@ func (in *ClusterFederatedTrustDomain) DeepCopyObject() runtime.Object {
|
|||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ClusterFederatedTrustDomainCustomValidator) DeepCopyInto(out *ClusterFederatedTrustDomainCustomValidator) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterFederatedTrustDomainCustomValidator.
|
||||
func (in *ClusterFederatedTrustDomainCustomValidator) DeepCopy() *ClusterFederatedTrustDomainCustomValidator {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ClusterFederatedTrustDomainCustomValidator)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ClusterFederatedTrustDomainList) DeepCopyInto(out *ClusterFederatedTrustDomainList) {
|
||||
*out = *in
|
||||
|
@ -160,6 +174,21 @@ func (in *ClusterSPIFFEID) DeepCopyObject() runtime.Object {
|
|||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ClusterSPIFFEIDCustomValidator) DeepCopyInto(out *ClusterSPIFFEIDCustomValidator) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterSPIFFEIDCustomValidator.
|
||||
func (in *ClusterSPIFFEIDCustomValidator) DeepCopy() *ClusterSPIFFEIDCustomValidator {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ClusterSPIFFEIDCustomValidator)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ClusterSPIFFEIDList) DeepCopyInto(out *ClusterSPIFFEIDList) {
|
||||
*out = *in
|
||||
|
@ -196,6 +225,7 @@ func (in *ClusterSPIFFEIDList) DeepCopyObject() runtime.Object {
|
|||
func (in *ClusterSPIFFEIDSpec) DeepCopyInto(out *ClusterSPIFFEIDSpec) {
|
||||
*out = *in
|
||||
out.TTL = in.TTL
|
||||
out.JWTTTL = in.JWTTTL
|
||||
if in.DNSNameTemplates != nil {
|
||||
in, out := &in.DNSNameTemplates, &out.DNSNameTemplates
|
||||
*out = make([]string, len(*in))
|
||||
|
@ -460,6 +490,22 @@ func (in *ControllerManagerConfigurationSpec) DeepCopyInto(out *ControllerManage
|
|||
*out = new(configv1alpha1.LeaderElectionConfiguration)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.CacheNamespaces != nil {
|
||||
in, out := &in.CacheNamespaces, &out.CacheNamespaces
|
||||
*out = make(map[string]*NamespaceConfig, len(*in))
|
||||
for key, val := range *in {
|
||||
var outVal *NamespaceConfig
|
||||
if val == nil {
|
||||
(*out)[key] = nil
|
||||
} else {
|
||||
inVal := (*in)[key]
|
||||
in, out := &inVal, &outVal
|
||||
*out = new(NamespaceConfig)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
(*out)[key] = outVal
|
||||
}
|
||||
}
|
||||
if in.GracefulShutdownTimeout != nil {
|
||||
in, out := &in.GracefulShutdownTimeout, &out.GracefulShutdownTimeout
|
||||
*out = new(v1.Duration)
|
||||
|
@ -473,6 +519,21 @@ func (in *ControllerManagerConfigurationSpec) DeepCopyInto(out *ControllerManage
|
|||
out.Metrics = in.Metrics
|
||||
out.Health = in.Health
|
||||
in.Webhook.DeepCopyInto(&out.Webhook)
|
||||
if in.Reconcile != nil {
|
||||
in, out := &in.Reconcile, &out.Reconcile
|
||||
*out = new(ReconcileConfig)
|
||||
**out = **in
|
||||
}
|
||||
if in.EntryIDPrefixCleanup != nil {
|
||||
in, out := &in.EntryIDPrefixCleanup, &out.EntryIDPrefixCleanup
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
if in.StaticManifestPath != nil {
|
||||
in, out := &in.StaticManifestPath, &out.StaticManifestPath
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControllerManagerConfigurationSpec.
|
||||
|
@ -519,3 +580,47 @@ func (in *ControllerWebhook) DeepCopy() *ControllerWebhook {
|
|||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NamespaceConfig) DeepCopyInto(out *NamespaceConfig) {
|
||||
*out = *in
|
||||
if in.LabelSelectors != nil {
|
||||
in, out := &in.LabelSelectors, &out.LabelSelectors
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.FieldSelectors != nil {
|
||||
in, out := &in.FieldSelectors, &out.FieldSelectors
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespaceConfig.
|
||||
func (in *NamespaceConfig) DeepCopy() *NamespaceConfig {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NamespaceConfig)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ReconcileConfig) DeepCopyInto(out *ReconcileConfig) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReconcileConfig.
|
||||
func (in *ReconcileConfig) DeepCopy() *ReconcileConfig {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ReconcileConfig)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
|
|
@ -0,0 +1,636 @@
|
|||
/*
|
||||
Copyright 2021 SPIRE Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
|
||||
// to ensure that exec-entrypoint and run can make use of them.
|
||||
"k8s.io/client-go/kubernetes"
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
"go.uber.org/zap/zapcore"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/healthz"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
k8sMetrics "sigs.k8s.io/controller-runtime/pkg/metrics"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||
|
||||
"github.com/spiffe/go-spiffe/v2/spiffeid"
|
||||
spirev1alpha1 "github.com/spiffe/spire-controller-manager/api/v1alpha1"
|
||||
"github.com/spiffe/spire-controller-manager/internal/controller"
|
||||
"github.com/spiffe/spire-controller-manager/pkg/metrics"
|
||||
"github.com/spiffe/spire-controller-manager/pkg/reconciler"
|
||||
"github.com/spiffe/spire-controller-manager/pkg/spireapi"
|
||||
"github.com/spiffe/spire-controller-manager/pkg/spireentry"
|
||||
"github.com/spiffe/spire-controller-manager/pkg/spirefederationrelationship"
|
||||
"github.com/spiffe/spire-controller-manager/pkg/webhookmanager"
|
||||
//+kubebuilder:scaffold:imports
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
ctrlConfig spirev1alpha1.ControllerManagerConfig
|
||||
options ctrl.Options
|
||||
ignoreNamespacesRegex []*regexp.Regexp
|
||||
parentIDTemplate *template.Template
|
||||
reconcile spirev1alpha1.ReconcileConfig
|
||||
}
|
||||
|
||||
const (
|
||||
defaultSPIREServerSocketPath = "/spire-server/api.sock"
|
||||
defaultGCInterval = 10 * time.Second
|
||||
defaultLogLevel = "info"
|
||||
defaultLogEncoding = "console"
|
||||
k8sDefaultService = "kubernetes.default.svc"
|
||||
)
|
||||
|
||||
var (
|
||||
scheme = runtime.NewScheme()
|
||||
setupLog = ctrl.Log.WithName("setup")
|
||||
)
|
||||
|
||||
func init() {
|
||||
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
|
||||
|
||||
utilruntime.Must(spirev1alpha1.AddToScheme(scheme))
|
||||
|
||||
k8sMetrics.Registry.MustRegister(
|
||||
metrics.PromCounters[metrics.StaticEntryFailures],
|
||||
)
|
||||
//+kubebuilder:scaffold:scheme
|
||||
}
|
||||
|
||||
func main() {
|
||||
mainConfig, err := parseConfig()
|
||||
if err != nil {
|
||||
setupLog.Error(err, "error parsing configuration")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if mainConfig.ctrlConfig.StaticManifestPath != nil {
|
||||
if err := staticRun(mainConfig); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if err := run(mainConfig); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func addDotSuffix(val string) string {
|
||||
if val != "" && !strings.HasSuffix(val, ".") {
|
||||
val += "."
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func parseConfig() (Config, error) {
|
||||
var retval Config
|
||||
var configFileFlag string
|
||||
var spireAPISocketFlag string
|
||||
var expandEnvFlag bool
|
||||
flag.StringVar(&configFileFlag, "config", "",
|
||||
"The controller will load its initial configuration from this file. "+
|
||||
"Omit this flag to use the default configuration values. "+
|
||||
"Command-line flags override configuration from this file.")
|
||||
flag.StringVar(&spireAPISocketFlag, "spire-api-socket", "", "The path to the SPIRE API socket (deprecated; use the config file)")
|
||||
flag.BoolVar(&expandEnvFlag, "expand-env", false, "Expand environment variables in SPIRE Controller Manager config file")
|
||||
opts := zap.Options{
|
||||
Development: true,
|
||||
}
|
||||
opts.BindFlags(flag.CommandLine)
|
||||
flag.Parse()
|
||||
|
||||
// Set default values
|
||||
retval.ctrlConfig = spirev1alpha1.ControllerManagerConfig{
|
||||
IgnoreNamespaces: []string{"kube-system", "kube-public", "spire-system"},
|
||||
GCInterval: defaultGCInterval,
|
||||
ValidatingWebhookConfigurationName: "spire-controller-manager-webhook",
|
||||
LogLevel: defaultLogLevel,
|
||||
LogEncoding: defaultLogEncoding,
|
||||
}
|
||||
|
||||
retval.options = ctrl.Options{Scheme: scheme}
|
||||
|
||||
// Setup logger to zap's default log level so errors parsing the config which contains the desired log level are logged
|
||||
_ = setLogger(&opts, "", "")
|
||||
|
||||
if configFileFlag != "" {
|
||||
if err := spirev1alpha1.LoadOptionsFromFile(configFileFlag, scheme, &retval.options, &retval.ctrlConfig, expandEnvFlag); err != nil {
|
||||
return retval, fmt.Errorf("unable to load the config file: %w", err)
|
||||
}
|
||||
for _, ignoredNamespace := range retval.ctrlConfig.IgnoreNamespaces {
|
||||
regex, err := regexp.Compile(ignoredNamespace)
|
||||
if err != nil {
|
||||
return retval, fmt.Errorf("unable to compile ignore namespaces regex: %w", err)
|
||||
}
|
||||
|
||||
retval.ignoreNamespacesRegex = append(retval.ignoreNamespacesRegex, regex)
|
||||
}
|
||||
}
|
||||
|
||||
// Parse log flags
|
||||
if err := setLogger(&opts, retval.ctrlConfig.LogLevel, retval.ctrlConfig.LogEncoding); err != nil {
|
||||
return retval, fmt.Errorf("unable to parse log level: %w", err)
|
||||
}
|
||||
setupLog.Info("Logger configured", "level", opts.Level)
|
||||
|
||||
// Determine the SPIRE Server socket path
|
||||
switch {
|
||||
case retval.ctrlConfig.SPIREServerSocketPath == "" && spireAPISocketFlag == "":
|
||||
// Neither is set. Use the default.
|
||||
retval.ctrlConfig.SPIREServerSocketPath = defaultSPIREServerSocketPath
|
||||
case retval.ctrlConfig.SPIREServerSocketPath != "" && spireAPISocketFlag == "":
|
||||
// Configuration file value is set. Use it.
|
||||
case retval.ctrlConfig.SPIREServerSocketPath == "" && spireAPISocketFlag != "":
|
||||
// Deprecated flag value is set. Use it but warn.
|
||||
retval.ctrlConfig.SPIREServerSocketPath = spireAPISocketFlag
|
||||
setupLog.Error(nil, "The spire-api-socket flag is deprecated and will be removed in a future release; use the configuration file instead")
|
||||
case retval.ctrlConfig.SPIREServerSocketPath != "" && spireAPISocketFlag != "":
|
||||
// Both are set. Warn and ignore the deprecated flag.
|
||||
setupLog.Error(nil, "Ignoring deprecated spire-api-socket flag which will be removed in a future release")
|
||||
}
|
||||
|
||||
// Attempt to auto detect cluster domain if it wasn't specified
|
||||
if retval.ctrlConfig.ClusterDomain == "" {
|
||||
clusterDomain, err := autoDetectClusterDomain()
|
||||
if err != nil {
|
||||
setupLog.Error(err, "unable to autodetect cluster domain")
|
||||
}
|
||||
|
||||
retval.ctrlConfig.ClusterDomain = clusterDomain
|
||||
}
|
||||
|
||||
if retval.ctrlConfig.ParentIDTemplate != "" {
|
||||
var err error
|
||||
retval.parentIDTemplate, err = template.New("customParentIDTemplate").Parse(retval.ctrlConfig.ParentIDTemplate)
|
||||
if err != nil {
|
||||
return retval, fmt.Errorf("unable to parse parent ID template: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if retval.ctrlConfig.Reconcile == nil {
|
||||
retval.reconcile.ClusterFederatedTrustDomains = true
|
||||
retval.reconcile.ClusterStaticEntries = true
|
||||
if retval.ctrlConfig.StaticManifestPath == nil {
|
||||
// Static mode default is to have ClusterSPIFFEID syncing off (unsupported). Non static mode syncing on.
|
||||
retval.reconcile.ClusterSPIFFEIDs = true
|
||||
}
|
||||
} else {
|
||||
retval.reconcile = *retval.ctrlConfig.Reconcile
|
||||
}
|
||||
|
||||
if retval.ctrlConfig.StaticManifestPath != nil {
|
||||
if retval.options.LeaderElection {
|
||||
return retval, fmt.Errorf("Leader election is not possible with static manifests")
|
||||
}
|
||||
if retval.reconcile.ClusterSPIFFEIDs {
|
||||
return retval, fmt.Errorf("ClusterSPIFFEID reconciliation is not possible with static manifests")
|
||||
}
|
||||
}
|
||||
|
||||
retval.ctrlConfig.EntryIDPrefix = addDotSuffix(retval.ctrlConfig.EntryIDPrefix)
|
||||
|
||||
printCleanup := "<unset>"
|
||||
if retval.ctrlConfig.EntryIDPrefixCleanup != nil {
|
||||
printCleanup = *retval.ctrlConfig.EntryIDPrefixCleanup
|
||||
*retval.ctrlConfig.EntryIDPrefixCleanup = addDotSuffix(*retval.ctrlConfig.EntryIDPrefixCleanup)
|
||||
if retval.ctrlConfig.EntryIDPrefix != "" && retval.ctrlConfig.EntryIDPrefix == *retval.ctrlConfig.EntryIDPrefixCleanup {
|
||||
return retval, fmt.Errorf("if entryIDPrefixCleanup is specified, it can not be the same value as entryIDPrefix")
|
||||
}
|
||||
}
|
||||
|
||||
setupLog.Info("Config loaded",
|
||||
"cluster name", retval.ctrlConfig.ClusterName,
|
||||
"cluster domain", retval.ctrlConfig.ClusterDomain,
|
||||
"trust domain", retval.ctrlConfig.TrustDomain,
|
||||
"ignore namespaces", retval.ctrlConfig.IgnoreNamespaces,
|
||||
"gc interval", retval.ctrlConfig.GCInterval,
|
||||
"spire server socket path", retval.ctrlConfig.SPIREServerSocketPath,
|
||||
"class name", retval.ctrlConfig.ClassName,
|
||||
"handle crs without class name", retval.ctrlConfig.WatchClassless,
|
||||
"reconcile ClusterSPIFFEIDs", retval.reconcile.ClusterSPIFFEIDs,
|
||||
"reconcile ClusterFederatedTrustDomains", retval.reconcile.ClusterFederatedTrustDomains,
|
||||
"reconcile ClusterStaticEntries", retval.reconcile.ClusterStaticEntries,
|
||||
"entryIDPrefix", retval.ctrlConfig.EntryIDPrefix,
|
||||
"entryIDPrefixCleanup", printCleanup)
|
||||
|
||||
switch {
|
||||
case retval.ctrlConfig.TrustDomain == "":
|
||||
setupLog.Error(nil, "trust domain is required configuration")
|
||||
return retval, errors.New("trust domain is required configuration")
|
||||
case retval.ctrlConfig.ClusterName == "":
|
||||
return retval, errors.New("cluster name is required configuration")
|
||||
case retval.ctrlConfig.ValidatingWebhookConfigurationName == "":
|
||||
return retval, errors.New("validating webhook configuration name is required configuration")
|
||||
case retval.ctrlConfig.ControllerManagerConfigurationSpec.Webhook.CertDir != "":
|
||||
setupLog.Info("certDir configuration is ignored", "certDir", retval.ctrlConfig.ControllerManagerConfigurationSpec.Webhook.CertDir)
|
||||
}
|
||||
|
||||
return retval, nil
|
||||
}
|
||||
|
||||
func run(mainConfig Config) (err error) {
|
||||
webhookEnabled := os.Getenv("ENABLE_WEBHOOKS") != "false"
|
||||
|
||||
trustDomain, err := spiffeid.TrustDomainFromString(mainConfig.ctrlConfig.TrustDomain)
|
||||
if err != nil {
|
||||
setupLog.Error(err, "invalid trust domain name")
|
||||
return err
|
||||
}
|
||||
|
||||
ctx := ctrl.SetupSignalHandler()
|
||||
|
||||
setupLog.Info("Dialing SPIRE Server socket")
|
||||
spireClient, err := spireapi.DialSocket(mainConfig.ctrlConfig.SPIREServerSocketPath)
|
||||
if err != nil {
|
||||
setupLog.Error(err, "unable to dial SPIRE Server socket")
|
||||
return err
|
||||
}
|
||||
defer spireClient.Close()
|
||||
|
||||
// It's unfortunate that we have to keep credentials on disk so that the
|
||||
// manager can load them. Webhook server credentials are stored in a single
|
||||
// file to keep rotation simple.
|
||||
// TODO: upstream a change to the WebhookServer so it can use callbacks to
|
||||
// obtain the certificates so we don't have to touch disk.
|
||||
var webhookManager *webhookmanager.Manager
|
||||
if webhookEnabled {
|
||||
const keyPairName = "keypair.pem"
|
||||
certDir, err := os.MkdirTemp("", "spire-controller-manager-")
|
||||
if err != nil {
|
||||
setupLog.Error(err, "failed to create temporary cert directory")
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err := os.RemoveAll(certDir); err != nil {
|
||||
setupLog.Error(err, "failed to remove temporary cert directory", "certDir", certDir)
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
mainConfig.options.WebhookServer = webhook.NewServer(webhook.Options{
|
||||
CertDir: certDir,
|
||||
CertName: keyPairName,
|
||||
KeyName: keyPairName,
|
||||
TLSOpts: []func(*tls.Config){
|
||||
func(s *tls.Config) {
|
||||
s.MinVersion = tls.VersionTLS12
|
||||
},
|
||||
},
|
||||
})
|
||||
// We need a direct client to query and patch up the webhook. We can't use
|
||||
// the controller runtime client for this because we can't start the manager
|
||||
// without the webhook credentials being in place, and the webhook credentials
|
||||
// need the DNS name of the webhook service from the configuration.
|
||||
config, err := rest.InClusterConfig()
|
||||
if err != nil {
|
||||
setupLog.Error(err, "failed to get in cluster configuration")
|
||||
return err
|
||||
}
|
||||
// creates the clientset
|
||||
clientset, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
setupLog.Error(err, "failed to create an API client")
|
||||
return err
|
||||
}
|
||||
|
||||
webhookManager = webhookmanager.New(webhookmanager.Config{
|
||||
ID: spiffeid.RequireFromPath(trustDomain, "/spire-controller-manager-webhook"),
|
||||
KeyPairPath: filepath.Join(certDir, keyPairName),
|
||||
WebhookName: mainConfig.ctrlConfig.ValidatingWebhookConfigurationName,
|
||||
WebhookClient: clientset.AdmissionregistrationV1().ValidatingWebhookConfigurations(),
|
||||
SVIDClient: spireClient,
|
||||
BundleClient: spireClient,
|
||||
})
|
||||
|
||||
if err := webhookManager.Init(ctx); err != nil {
|
||||
setupLog.Error(err, "failed to mint initial webhook certificate")
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), mainConfig.options)
|
||||
if err != nil {
|
||||
setupLog.Error(err, "unable to start manager")
|
||||
return err
|
||||
}
|
||||
|
||||
var entryReconciler reconciler.Reconciler
|
||||
if mainConfig.reconcile.ClusterSPIFFEIDs || mainConfig.reconcile.ClusterStaticEntries {
|
||||
entryReconciler = spireentry.Reconciler(spireentry.ReconcilerConfig{
|
||||
TrustDomain: trustDomain,
|
||||
ClusterName: mainConfig.ctrlConfig.ClusterName,
|
||||
ClusterDomain: mainConfig.ctrlConfig.ClusterDomain,
|
||||
K8sClient: mgr.GetClient(),
|
||||
EntryClient: spireClient,
|
||||
IgnoreNamespaces: mainConfig.ignoreNamespacesRegex,
|
||||
GCInterval: mainConfig.ctrlConfig.GCInterval,
|
||||
ClassName: mainConfig.ctrlConfig.ClassName,
|
||||
WatchClassless: mainConfig.ctrlConfig.WatchClassless,
|
||||
ParentIDTemplate: mainConfig.parentIDTemplate,
|
||||
Reconcile: mainConfig.reconcile,
|
||||
EntryIDPrefix: mainConfig.ctrlConfig.EntryIDPrefix,
|
||||
EntryIDPrefixCleanup: mainConfig.ctrlConfig.EntryIDPrefixCleanup,
|
||||
})
|
||||
}
|
||||
|
||||
var federationRelationshipReconciler reconciler.Reconciler
|
||||
if mainConfig.reconcile.ClusterFederatedTrustDomains {
|
||||
federationRelationshipReconciler = spirefederationrelationship.Reconciler(spirefederationrelationship.ReconcilerConfig{
|
||||
K8sClient: mgr.GetClient(),
|
||||
TrustDomainClient: spireClient,
|
||||
GCInterval: mainConfig.ctrlConfig.GCInterval,
|
||||
ClassName: mainConfig.ctrlConfig.ClassName,
|
||||
WatchClassless: mainConfig.ctrlConfig.WatchClassless,
|
||||
})
|
||||
if err = (&controller.ClusterFederatedTrustDomainReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
Triggerer: federationRelationshipReconciler,
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "ClusterFederatedTrustDomain")
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if mainConfig.reconcile.ClusterSPIFFEIDs {
|
||||
if err = (&controller.ClusterSPIFFEIDReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
Triggerer: entryReconciler,
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "ClusterSPIFFEID")
|
||||
return err
|
||||
}
|
||||
}
|
||||
if mainConfig.reconcile.ClusterStaticEntries {
|
||||
if err = (&controller.ClusterStaticEntryReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
Triggerer: entryReconciler,
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "ClusterStaticEntry")
|
||||
return err
|
||||
}
|
||||
}
|
||||
if webhookEnabled {
|
||||
if err = (&spirev1alpha1.ClusterFederatedTrustDomain{}).SetupWebhookWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create webhook", "webhook", "ClusterFederatedTrustDomain")
|
||||
return err
|
||||
}
|
||||
if err = (&spirev1alpha1.ClusterSPIFFEID{}).SetupWebhookWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create webhook", "webhook", "ClusterSPIFFEID")
|
||||
return err
|
||||
}
|
||||
}
|
||||
//+kubebuilder:scaffold:builder
|
||||
|
||||
if mainConfig.reconcile.ClusterSPIFFEIDs {
|
||||
if err = (&controller.PodReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
Triggerer: entryReconciler,
|
||||
IgnoreNamespaces: mainConfig.ignoreNamespacesRegex,
|
||||
}).SetupWithManager(ctx, mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "Pod")
|
||||
return err
|
||||
}
|
||||
if err = (&controller.EndpointsReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
Triggerer: entryReconciler,
|
||||
IgnoreNamespaces: mainConfig.ignoreNamespacesRegex,
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "Endpoints")
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if entryReconciler != nil {
|
||||
if err = mgr.Add(manager.RunnableFunc(entryReconciler.Run)); err != nil {
|
||||
setupLog.Error(err, "unable to manage entry reconciler")
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if federationRelationshipReconciler != nil {
|
||||
if err = mgr.Add(manager.RunnableFunc(federationRelationshipReconciler.Run)); err != nil {
|
||||
setupLog.Error(err, "unable to manage federation relationship reconciler")
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if webhookManager != nil {
|
||||
if err = mgr.Add(webhookManager); err != nil {
|
||||
setupLog.Error(err, "unable to manage webhook")
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
|
||||
setupLog.Error(err, "unable to set up health check")
|
||||
return err
|
||||
}
|
||||
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
|
||||
setupLog.Error(err, "unable to set up ready check")
|
||||
return err
|
||||
}
|
||||
|
||||
setupLog.Info("starting manager")
|
||||
if err := mgr.Start(ctx); err != nil {
|
||||
setupLog.Error(err, "problem running manager")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func staticRun(mainConfig Config) (err error) {
|
||||
var wg sync.WaitGroup
|
||||
if mainConfig.reconcile.ClusterFederatedTrustDomains {
|
||||
wg.Add(1)
|
||||
}
|
||||
if mainConfig.reconcile.ClusterStaticEntries {
|
||||
wg.Add(1)
|
||||
}
|
||||
trustDomain, err := spiffeid.TrustDomainFromString(mainConfig.ctrlConfig.TrustDomain)
|
||||
if err != nil {
|
||||
setupLog.Error(err, "invalid trust domain name")
|
||||
return err
|
||||
}
|
||||
|
||||
ctx := ctrl.SetupSignalHandler()
|
||||
|
||||
setupLog.Info("Dialing SPIRE Server socket")
|
||||
spireClient, err := spireapi.DialSocket(mainConfig.ctrlConfig.SPIREServerSocketPath)
|
||||
if err != nil {
|
||||
setupLog.Error(err, "unable to dial SPIRE Server socket")
|
||||
return err
|
||||
}
|
||||
defer spireClient.Close()
|
||||
|
||||
mgr, err := ctrl.NewManager(&rest.Config{}, mainConfig.options)
|
||||
if err != nil {
|
||||
setupLog.Error(err, "unable to start manager")
|
||||
return err
|
||||
}
|
||||
if mainConfig.reconcile.ClusterStaticEntries {
|
||||
entryReconciler := spireentry.Reconciler(spireentry.ReconcilerConfig{
|
||||
TrustDomain: trustDomain,
|
||||
ClusterName: mainConfig.ctrlConfig.ClusterName,
|
||||
ClusterDomain: mainConfig.ctrlConfig.ClusterDomain,
|
||||
K8sClient: nil,
|
||||
EntryClient: spireClient,
|
||||
IgnoreNamespaces: mainConfig.ignoreNamespacesRegex,
|
||||
GCInterval: mainConfig.ctrlConfig.GCInterval,
|
||||
ClassName: mainConfig.ctrlConfig.ClassName,
|
||||
WatchClassless: mainConfig.ctrlConfig.WatchClassless,
|
||||
ParentIDTemplate: mainConfig.parentIDTemplate,
|
||||
Reconcile: mainConfig.reconcile,
|
||||
EntryIDPrefix: mainConfig.ctrlConfig.EntryIDPrefix,
|
||||
EntryIDPrefixCleanup: mainConfig.ctrlConfig.EntryIDPrefixCleanup,
|
||||
StaticManifestPath: mainConfig.ctrlConfig.StaticManifestPath,
|
||||
})
|
||||
go func() {
|
||||
err = entryReconciler.Run(ctx)
|
||||
if err != nil {
|
||||
setupLog.Error(err, "failure starting entry reconciler", "controller", "ClusterStaticEntry")
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
if mainConfig.reconcile.ClusterFederatedTrustDomains {
|
||||
federationRelationshipReconciler := spirefederationrelationship.Reconciler(spirefederationrelationship.ReconcilerConfig{
|
||||
K8sClient: nil,
|
||||
TrustDomainClient: spireClient,
|
||||
GCInterval: mainConfig.ctrlConfig.GCInterval,
|
||||
ClassName: mainConfig.ctrlConfig.ClassName,
|
||||
WatchClassless: mainConfig.ctrlConfig.WatchClassless,
|
||||
StaticManifestPath: mainConfig.ctrlConfig.StaticManifestPath,
|
||||
})
|
||||
go func() {
|
||||
err = federationRelationshipReconciler.Run(ctx)
|
||||
if err != nil {
|
||||
setupLog.Error(err, "failure starting federation relationship reconciler", "controller", "ClusterFederatedTrustDomain")
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
|
||||
setupLog.Error(err, "unable to set up health check")
|
||||
return err
|
||||
}
|
||||
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
|
||||
setupLog.Error(err, "unable to set up ready check")
|
||||
return err
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
setupLog.Info("starting manager")
|
||||
if err := mgr.Start(ctx); err != nil {
|
||||
setupLog.Error(err, "problem running manager")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func autoDetectClusterDomain() (string, error) {
|
||||
cname, err := net.LookupCNAME(k8sDefaultService)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to lookup CNAME: %w", err)
|
||||
}
|
||||
|
||||
clusterDomain, err := parseClusterDomainCNAME(cname)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to parse CNAME \"%s\": %w", cname, err)
|
||||
}
|
||||
|
||||
return clusterDomain, nil
|
||||
}
|
||||
|
||||
func parseClusterDomainCNAME(cname string) (string, error) {
|
||||
clusterDomain := strings.TrimPrefix(cname, k8sDefaultService+".")
|
||||
if clusterDomain == cname {
|
||||
return "", errors.New("CNAME did not have expected prefix")
|
||||
}
|
||||
|
||||
// Trim off optional trailing dot
|
||||
clusterDomain = strings.TrimSuffix(clusterDomain, ".")
|
||||
if clusterDomain == "" {
|
||||
return "", errors.New("CNAME did not have a cluster domain")
|
||||
}
|
||||
|
||||
return clusterDomain, nil
|
||||
}
|
||||
|
||||
func setLogger(opts *zap.Options, logLevel string, logEncoding string) error {
|
||||
if logLevel != "" && opts.Level == nil {
|
||||
zapLogLevel, err := getLogLevel(logLevel)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse log level: %w", err)
|
||||
}
|
||||
opts.Level = zapLogLevel
|
||||
}
|
||||
if logEncoding != "" && opts.Encoder == nil {
|
||||
switch logEncoding {
|
||||
case "console":
|
||||
zap.ConsoleEncoder(opts.EncoderConfigOptions...)(opts)
|
||||
case "json":
|
||||
zap.JSONEncoder(opts.EncoderConfigOptions...)(opts)
|
||||
default:
|
||||
return fmt.Errorf("unrecognized log encoding: %s", logEncoding)
|
||||
}
|
||||
}
|
||||
|
||||
ctrl.SetLogger(zap.New(zap.UseFlagOptions(opts)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLogLevel(logLevel string) (zapcore.Level, error) {
|
||||
switch strings.ToLower(logLevel) {
|
||||
case "debug":
|
||||
return zapcore.DebugLevel, nil
|
||||
case "warn":
|
||||
return zapcore.WarnLevel, nil
|
||||
case "error":
|
||||
return zapcore.ErrorLevel, nil
|
||||
case "info":
|
||||
return zapcore.InfoLevel, nil
|
||||
default:
|
||||
return zapcore.InfoLevel, fmt.Errorf("invalid log level: %q", logLevel)
|
||||
}
|
||||
}
|
|
@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
|||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.11.1
|
||||
creationTimestamp: null
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
name: clusterfederatedtrustdomains.spire.spiffe.io
|
||||
spec:
|
||||
group: spire.spiffe.io
|
||||
|
@ -29,14 +28,19 @@ spec:
|
|||
API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
|
@ -48,8 +52,9 @@ spec:
|
|||
description: BundleEndpointProfile is the profile for the bundle endpoint.
|
||||
properties:
|
||||
endpointSPIFFEID:
|
||||
description: EndpointSPIFFEID is the SPIFFE ID of the bundle endpoint.
|
||||
It is required for the "https_spiffe" profile.
|
||||
description: |-
|
||||
EndpointSPIFFEID is the SPIFFE ID of the bundle endpoint. It is
|
||||
required for the "https_spiffe" profile.
|
||||
type: string
|
||||
type:
|
||||
description: Type is the type of the bundle endpoint profile.
|
||||
|
@ -61,8 +66,12 @@ spec:
|
|||
- type
|
||||
type: object
|
||||
bundleEndpointURL:
|
||||
description: BundleEndpointURL is the URL of the bundle endpoint.
|
||||
It must be an HTTPS URL and cannot contain userinfo (i.e. username/password).
|
||||
description: |-
|
||||
BundleEndpointURL is the URL of the bundle endpoint. It must be an
|
||||
HTTPS URL and cannot contain userinfo (i.e. username/password).
|
||||
type: string
|
||||
className:
|
||||
description: Set which Controller Class will act on this object
|
||||
type: string
|
||||
trustDomain:
|
||||
description: TrustDomain is the name of the trust domain to federate
|
||||
|
@ -70,9 +79,9 @@ spec:
|
|||
pattern: '[a-z0-9._-]{1,255}'
|
||||
type: string
|
||||
trustDomainBundle:
|
||||
description: TrustDomainBundle is the contents of the bundle for the
|
||||
referenced trust domain. This field is optional when the resource
|
||||
is created.
|
||||
description: |-
|
||||
TrustDomainBundle is the contents of the bundle for the referenced trust
|
||||
domain. This field is optional when the resource is created.
|
||||
type: string
|
||||
required:
|
||||
- bundleEndpointProfile
|
||||
|
@ -88,3 +97,9 @@ spec:
|
|||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
|
|
|
@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
|||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.11.1
|
||||
creationTimestamp: null
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
name: clusterspiffeids.spire.spiffe.io
|
||||
spec:
|
||||
group: spire.spiffe.io
|
||||
|
@ -21,14 +20,19 @@ spec:
|
|||
description: ClusterSPIFFEID is the Schema for the clusterspiffeids API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
|
@ -36,15 +40,29 @@ spec:
|
|||
description: ClusterSPIFFEIDSpec defines the desired state of ClusterSPIFFEID
|
||||
properties:
|
||||
admin:
|
||||
description: Admin indicates whether or not the SVID can be used to
|
||||
access the SPIRE administrative APIs. Extra care should be taken
|
||||
to only apply this SPIFFE ID to admin workloads.
|
||||
description: |-
|
||||
Admin indicates whether or not the SVID can be used to access the SPIRE
|
||||
administrative APIs. Extra care should be taken to only apply this
|
||||
SPIFFE ID to admin workloads.
|
||||
type: boolean
|
||||
autoPopulateDNSNames:
|
||||
description: AutoPopulateDNSNames indicates whether or not to auto
|
||||
populate service DNS names.
|
||||
type: boolean
|
||||
className:
|
||||
description: Set which Controller Class will act on this object
|
||||
type: string
|
||||
fallback:
|
||||
description: |-
|
||||
Apply this ID only if there are no other matching non fallback
|
||||
ClusterSPIFFEIDs
|
||||
type: boolean
|
||||
dnsNameTemplates:
|
||||
description: DNSNameTemplate represents templates for extra DNS names
|
||||
that are applicable to SVIDs minted for this ClusterSPIFFEID. The
|
||||
node and pod spec are made available to the template under .NodeSpec,
|
||||
.PodSpec respectively.
|
||||
description: |-
|
||||
DNSNameTemplate represents templates for extra DNS names that are
|
||||
applicable to SVIDs minted for this ClusterSPIFFEID.
|
||||
The node and pod spec are made available to the template under
|
||||
.NodeSpec, .PodSpec respectively.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
|
@ -53,87 +71,48 @@ spec:
|
|||
SPIRE server.
|
||||
type: boolean
|
||||
federatesWith:
|
||||
description: FederatesWith is a list of trust domain names that workloads
|
||||
that obtain this SPIFFE ID will federate with.
|
||||
description: |-
|
||||
FederatesWith is a list of trust domain names that workloads that
|
||||
obtain this SPIFFE ID will federate with.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
hint:
|
||||
description: |-
|
||||
Set the entry hint
|
||||
type: string
|
||||
jwtTtl:
|
||||
description: JWTTTL indicates an upper-bound time-to-live for JWT
|
||||
SVIDs minted for this ClusterSPIFFEID.
|
||||
description: |-
|
||||
JWTTTL indicates an upper-bound time-to-live for JWT SVIDs minted for this
|
||||
ClusterSPIFFEID.
|
||||
type: string
|
||||
namespaceSelector:
|
||||
description: NamespaceSelector selects the namespaces that are targeted
|
||||
by this CRD.
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector requirements.
|
||||
The requirements are ANDed.
|
||||
items:
|
||||
description: A label selector requirement is a selector that
|
||||
contains values, a key, and an operator that relates the key
|
||||
and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector applies
|
||||
to.
|
||||
type: string
|
||||
operator:
|
||||
description: operator represents a key's relationship to
|
||||
a set of values. Valid operators are In, NotIn, Exists
|
||||
and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: values is an array of string values. If the
|
||||
operator is In or NotIn, the values array must be non-empty.
|
||||
If the operator is Exists or DoesNotExist, the values
|
||||
array must be empty. This array is replaced during a strategic
|
||||
merge patch.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- key
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: matchLabels is a map of {key,value} pairs. A single
|
||||
{key,value} in the matchLabels map is equivalent to an element
|
||||
of matchExpressions, whose key field is "key", the operator
|
||||
is "In", and the values array contains only "value". The requirements
|
||||
are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
podSelector:
|
||||
description: PodSelector selects the pods that are targeted by this
|
||||
description: |-
|
||||
NamespaceSelector selects the namespaces that are targeted by this
|
||||
CRD.
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector requirements.
|
||||
The requirements are ANDed.
|
||||
items:
|
||||
description: A label selector requirement is a selector that
|
||||
contains values, a key, and an operator that relates the key
|
||||
and values.
|
||||
description: |-
|
||||
A label selector requirement is a selector that contains values, a key, and an operator that
|
||||
relates the key and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector applies
|
||||
to.
|
||||
type: string
|
||||
operator:
|
||||
description: operator represents a key's relationship to
|
||||
a set of values. Valid operators are In, NotIn, Exists
|
||||
and DoesNotExist.
|
||||
description: |-
|
||||
operator represents a key's relationship to a set of values.
|
||||
Valid operators are In, NotIn, Exists and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: values is an array of string values. If the
|
||||
operator is In or NotIn, the values array must be non-empty.
|
||||
If the operator is Exists or DoesNotExist, the values
|
||||
array must be empty. This array is replaced during a strategic
|
||||
description: |-
|
||||
values is an array of string values. If the operator is In or NotIn,
|
||||
the values array must be non-empty. If the operator is Exists or DoesNotExist,
|
||||
the values array must be empty. This array is replaced during a strategic
|
||||
merge patch.
|
||||
items:
|
||||
type: string
|
||||
|
@ -146,31 +125,78 @@ spec:
|
|||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: matchLabels is a map of {key,value} pairs. A single
|
||||
{key,value} in the matchLabels map is equivalent to an element
|
||||
of matchExpressions, whose key field is "key", the operator
|
||||
is "In", and the values array contains only "value". The requirements
|
||||
are ANDed.
|
||||
description: |-
|
||||
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
|
||||
map is equivalent to an element of matchExpressions, whose key field is "key", the
|
||||
operator is "In", and the values array contains only "value". The requirements are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
podSelector:
|
||||
description: |-
|
||||
PodSelector selects the pods that are targeted by this
|
||||
CRD.
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector requirements.
|
||||
The requirements are ANDed.
|
||||
items:
|
||||
description: |-
|
||||
A label selector requirement is a selector that contains values, a key, and an operator that
|
||||
relates the key and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector applies
|
||||
to.
|
||||
type: string
|
||||
operator:
|
||||
description: |-
|
||||
operator represents a key's relationship to a set of values.
|
||||
Valid operators are In, NotIn, Exists and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: |-
|
||||
values is an array of string values. If the operator is In or NotIn,
|
||||
the values array must be non-empty. If the operator is Exists or DoesNotExist,
|
||||
the values array must be empty. This array is replaced during a strategic
|
||||
merge patch.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- key
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: |-
|
||||
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
|
||||
map is equivalent to an element of matchExpressions, whose key field is "key", the
|
||||
operator is "In", and the values array contains only "value". The requirements are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
spiffeIDTemplate:
|
||||
description: SPIFFEID is the SPIFFE ID template. The node and pod
|
||||
spec are made available to the template under .NodeSpec, .PodSpec
|
||||
respectively.
|
||||
description: |-
|
||||
SPIFFEID is the SPIFFE ID template. The node and pod spec are made
|
||||
available to the template under .NodeSpec, .PodSpec respectively.
|
||||
type: string
|
||||
ttl:
|
||||
description: TTL indicates an upper-bound time-to-live for X509 SVIDs
|
||||
minted for this ClusterSPIFFEID. If unset, a default will be chosen.
|
||||
description: |-
|
||||
TTL indicates an upper-bound time-to-live for X509 SVIDs minted for this
|
||||
ClusterSPIFFEID. If unset, a default will be chosen.
|
||||
type: string
|
||||
workloadSelectorTemplates:
|
||||
description: WorkloadSelectorTemplates are templates to produce arbitrary
|
||||
workload selectors that apply to a given workload before it will
|
||||
receive this SPIFFE ID. The rendered value is interpreted by SPIRE
|
||||
and are of the form type:value, where the value may, and often does,
|
||||
contain semicolons, .e.g., k8s:container-image:docker/hello-world
|
||||
The node and pod spec are made available to the template under .NodeSpec,
|
||||
.PodSpec respectively.
|
||||
description: |-
|
||||
WorkloadSelectorTemplates are templates to produce arbitrary workload
|
||||
selectors that apply to a given workload before it will receive this
|
||||
SPIFFE ID. The rendered value is interpreted by SPIRE and are of the
|
||||
form type:value, where the value may, and often does, contain
|
||||
semicolons, .e.g., k8s:container-image:docker/hello-world
|
||||
The node and pod spec are made available to the template under
|
||||
.NodeSpec, .PodSpec respectively.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
|
@ -184,21 +210,22 @@ spec:
|
|||
description: Stats produced by the last entry reconciliation run
|
||||
properties:
|
||||
entriesMasked:
|
||||
description: How many entries were masked by entries for other
|
||||
ClusterSPIFFEIDs. This happens when one or more ClusterSPIFFEIDs
|
||||
produce an entry for the same pod with the same set of workload
|
||||
selectors.
|
||||
description: |-
|
||||
How many entries were masked by entries for other ClusterSPIFFEIDs.
|
||||
This happens when one or more ClusterSPIFFEIDs produce an entry for
|
||||
the same pod with the same set of workload selectors.
|
||||
type: integer
|
||||
entriesToSet:
|
||||
description: How many entries are to be set for this ClusterSPIFFEID.
|
||||
In nominal conditions, this should reflect the number of pods
|
||||
selected, but not always if there were problems encountered
|
||||
rendering an entry for the pod (RenderFailures) or entries are
|
||||
masked (EntriesMasked).
|
||||
description: |-
|
||||
How many entries are to be set for this ClusterSPIFFEID. In nominal
|
||||
conditions, this should reflect the number of pods selected, but not
|
||||
always if there were problems encountered rendering an entry for the pod
|
||||
(RenderFailures) or entries are masked (EntriesMasked).
|
||||
type: integer
|
||||
entryFailures:
|
||||
description: How many entries were unable to be set due to failures
|
||||
to create or update the entries via the SPIRE Server API.
|
||||
description: |-
|
||||
How many entries were unable to be set due to failures to create or
|
||||
update the entries via the SPIRE Server API.
|
||||
type: integer
|
||||
namespacesIgnored:
|
||||
description: How many (selected) namespaces were ignored (based
|
||||
|
@ -208,10 +235,11 @@ spec:
|
|||
description: How many namespaces were selected.
|
||||
type: integer
|
||||
podEntryRenderFailures:
|
||||
description: How many failures were encountered rendering an entry
|
||||
selected pods. This could be due to either a bad template in
|
||||
the ClusterSPIFFEID or Pod metadata that when applied to the
|
||||
template did not produce valid entry values.
|
||||
description: |-
|
||||
How many failures were encountered rendering an entry selected pods.
|
||||
This could be due to either a bad template in the ClusterSPIFFEID or
|
||||
Pod metadata that when applied to the template did not produce valid
|
||||
entry values.
|
||||
type: integer
|
||||
podsSelected:
|
||||
description: How many pods were selected out of the namespaces.
|
||||
|
@ -223,3 +251,9 @@ spec:
|
|||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
|
|
|
@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
|||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.11.1
|
||||
creationTimestamp: null
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
name: clusterstaticentries.spire.spiffe.io
|
||||
spec:
|
||||
group: spire.spiffe.io
|
||||
|
@ -22,14 +21,19 @@ spec:
|
|||
API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
|
@ -38,6 +42,9 @@ spec:
|
|||
properties:
|
||||
admin:
|
||||
type: boolean
|
||||
className:
|
||||
description: Set which Controller Class will act on this object
|
||||
type: string
|
||||
dnsNames:
|
||||
items:
|
||||
type: string
|
||||
|
@ -60,6 +67,8 @@ spec:
|
|||
type: array
|
||||
spiffeID:
|
||||
type: string
|
||||
storeSVID:
|
||||
type: boolean
|
||||
x509SVIDTTL:
|
||||
type: string
|
||||
required:
|
||||
|
@ -89,3 +98,9 @@ spec:
|
|||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
|
|
|
@ -2,9 +2,16 @@
|
|||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: manager-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- endpoints
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
|
@ -29,6 +36,15 @@ rules:
|
|||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- admissionregistration.k8s.io
|
||||
resources:
|
||||
- validatingwebhookconfigurations
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- watch
|
||||
- apiGroups:
|
||||
- spire.spiffe.io
|
||||
resources:
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: ValidatingWebhookConfiguration
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: validating-webhook-configuration
|
||||
webhooks:
|
||||
- admissionReviewVersions:
|
||||
|
|
|
@ -33,9 +33,9 @@ Build the greeter server and client:
|
|||
|
||||
Pull the requisite images:
|
||||
|
||||
$ echo ghcr.io/spiffe/spire-server:1.7.0 \
|
||||
ghcr.io/spiffe/spire-agent:1.7.0 \
|
||||
ghcr.io/spiffe/spiffe-csi-driver:0.2.3 \
|
||||
$ echo ghcr.io/spiffe/spire-server:1.10.4 \
|
||||
ghcr.io/spiffe/spire-agent:1.10.4 \
|
||||
ghcr.io/spiffe/spiffe-csi-driver:0.2.6 \
|
||||
ghcr.io/spiffe/spire-controller-manager:nightly \
|
||||
| xargs -n1 docker pull
|
||||
|
||||
|
@ -43,9 +43,9 @@ Start up cluster1 and load the requisite images:
|
|||
|
||||
$ ./cluster1 kind create cluster
|
||||
$ echo \
|
||||
ghcr.io/spiffe/spire-server:1.7.0 \
|
||||
ghcr.io/spiffe/spire-agent:1.7.0 \
|
||||
ghcr.io/spiffe/spiffe-csi-driver:0.2.3 \
|
||||
ghcr.io/spiffe/spire-server:1.10.4 \
|
||||
ghcr.io/spiffe/spire-agent:1.10.4 \
|
||||
ghcr.io/spiffe/spiffe-csi-driver:0.2.6 \
|
||||
ghcr.io/spiffe/spire-controller-manager:nightly \
|
||||
greeter-server:demo \
|
||||
| xargs -n1 ./cluster1 kind load docker-image
|
||||
|
@ -54,9 +54,9 @@ Start up cluster 2 and load the requisite images:
|
|||
|
||||
$ ./cluster2 kind create cluster
|
||||
$ echo \
|
||||
ghcr.io/spiffe/spire-server:1.7.0 \
|
||||
ghcr.io/spiffe/spire-agent:1.7.0 \
|
||||
ghcr.io/spiffe/spiffe-csi-driver:0.2.3 \
|
||||
ghcr.io/spiffe/spire-server:1.10.4 \
|
||||
ghcr.io/spiffe/spire-agent:1.10.4 \
|
||||
ghcr.io/spiffe/spiffe-csi-driver:0.2.6 \
|
||||
ghcr.io/spiffe/spire-controller-manager:nightly \
|
||||
greeter-client:demo \
|
||||
| xargs -n1 ./cluster2 kind load docker-image
|
||||
|
|
|
@ -15,6 +15,9 @@ rules:
|
|||
- apiGroups: [""]
|
||||
resources: ["pods"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: [""]
|
||||
resources: ["endpoints"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: ["spire.spiffe.io"]
|
||||
resources: ["clusterfederatedtrustdomains"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||
|
|
|
@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
|||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.8.0
|
||||
creationTimestamp: null
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
name: clusterfederatedtrustdomains.spire.spiffe.io
|
||||
spec:
|
||||
group: spire.spiffe.io
|
||||
|
@ -64,6 +63,9 @@ spec:
|
|||
description: BundleEndpointURL is the URL of the bundle endpoint.
|
||||
It must be an HTTPS URL and cannot contain userinfo (i.e. username/password).
|
||||
type: string
|
||||
className:
|
||||
description: Set which Controller Class will act on this object
|
||||
type: string
|
||||
trustDomain:
|
||||
description: TrustDomain is the name of the trust domain to federate
|
||||
with (e.g. example.org)
|
||||
|
|
|
@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
|||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.8.0
|
||||
creationTimestamp: null
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
name: clusterspiffeids.spire.spiffe.io
|
||||
spec:
|
||||
group: spire.spiffe.io
|
||||
|
@ -21,14 +20,19 @@ spec:
|
|||
description: ClusterSPIFFEID is the Schema for the clusterspiffeids API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
|
@ -36,102 +40,79 @@ spec:
|
|||
description: ClusterSPIFFEIDSpec defines the desired state of ClusterSPIFFEID
|
||||
properties:
|
||||
admin:
|
||||
description: Admin indicates whether or not the SVID can be used to
|
||||
access the SPIRE administrative APIs. Extra care should be taken
|
||||
to only apply this SPIFFE ID to admin workloads.
|
||||
description: |-
|
||||
Admin indicates whether or not the SVID can be used to access the SPIRE
|
||||
administrative APIs. Extra care should be taken to only apply this
|
||||
SPIFFE ID to admin workloads.
|
||||
type: boolean
|
||||
autoPopulateDNSNames:
|
||||
description: AutoPopulateDNSNames indicates whether or not to auto
|
||||
populate service DNS names.
|
||||
type: boolean
|
||||
className:
|
||||
description: Set which Controller Class will act on this object
|
||||
type: string
|
||||
fallback:
|
||||
description: |-
|
||||
Apply this ID only if there are no other matching non fallback
|
||||
ClusterSPIFFEIDs
|
||||
type: boolean
|
||||
dnsNameTemplates:
|
||||
description: DNSNameTemplate represents templates for extra DNS names
|
||||
that are applicable to SVIDs minted for this ClusterSPIFFEID. The
|
||||
node and pod spec are made available to the template under .NodeSpec,
|
||||
.PodSpec respectively.
|
||||
description: |-
|
||||
DNSNameTemplate represents templates for extra DNS names that are
|
||||
applicable to SVIDs minted for this ClusterSPIFFEID.
|
||||
The node and pod spec are made available to the template under
|
||||
.NodeSpec, .PodSpec respectively.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
downstream:
|
||||
description: Downstream indicates that the entry describes a downstream SPIRE server.
|
||||
description: Downstream indicates that the entry describes a downstream
|
||||
SPIRE server.
|
||||
type: boolean
|
||||
federatesWith:
|
||||
description: FederatesWith is a list of trust domain names that workloads
|
||||
that obtain this SPIFFE ID will federate with.
|
||||
description: |-
|
||||
FederatesWith is a list of trust domain names that workloads that
|
||||
obtain this SPIFFE ID will federate with.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
hint:
|
||||
description: |-
|
||||
Set the entry hint
|
||||
type: string
|
||||
jwtTtl:
|
||||
description: JWTTTL indicates an upper-bound time-to-live for JWT
|
||||
SVIDs minted for this ClusterSPIFFEID.
|
||||
description: |-
|
||||
JWTTTL indicates an upper-bound time-to-live for JWT SVIDs minted for this
|
||||
ClusterSPIFFEID.
|
||||
type: string
|
||||
namespaceSelector:
|
||||
description: NamespaceSelector selects the namespaces that are targetted
|
||||
by this CRD.
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector requirements.
|
||||
The requirements are ANDed.
|
||||
items:
|
||||
description: A label selector requirement is a selector that
|
||||
contains values, a key, and an operator that relates the key
|
||||
and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector applies
|
||||
to.
|
||||
type: string
|
||||
operator:
|
||||
description: operator represents a key's relationship to
|
||||
a set of values. Valid operators are In, NotIn, Exists
|
||||
and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: values is an array of string values. If the
|
||||
operator is In or NotIn, the values array must be non-empty.
|
||||
If the operator is Exists or DoesNotExist, the values
|
||||
array must be empty. This array is replaced during a strategic
|
||||
merge patch.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- key
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: matchLabels is a map of {key,value} pairs. A single
|
||||
{key,value} in the matchLabels map is equivalent to an element
|
||||
of matchExpressions, whose key field is "key", the operator
|
||||
is "In", and the values array contains only "value". The requirements
|
||||
are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
podSelector:
|
||||
description: PodSelector selects the pods that are targetted by this
|
||||
description: |-
|
||||
NamespaceSelector selects the namespaces that are targeted by this
|
||||
CRD.
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector requirements.
|
||||
The requirements are ANDed.
|
||||
items:
|
||||
description: A label selector requirement is a selector that
|
||||
contains values, a key, and an operator that relates the key
|
||||
and values.
|
||||
description: |-
|
||||
A label selector requirement is a selector that contains values, a key, and an operator that
|
||||
relates the key and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector applies
|
||||
to.
|
||||
type: string
|
||||
operator:
|
||||
description: operator represents a key's relationship to
|
||||
a set of values. Valid operators are In, NotIn, Exists
|
||||
and DoesNotExist.
|
||||
description: |-
|
||||
operator represents a key's relationship to a set of values.
|
||||
Valid operators are In, NotIn, Exists and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: values is an array of string values. If the
|
||||
operator is In or NotIn, the values array must be non-empty.
|
||||
If the operator is Exists or DoesNotExist, the values
|
||||
array must be empty. This array is replaced during a strategic
|
||||
description: |-
|
||||
values is an array of string values. If the operator is In or NotIn,
|
||||
the values array must be non-empty. If the operator is Exists or DoesNotExist,
|
||||
the values array must be empty. This array is replaced during a strategic
|
||||
merge patch.
|
||||
items:
|
||||
type: string
|
||||
|
@ -144,30 +125,78 @@ spec:
|
|||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: matchLabels is a map of {key,value} pairs. A single
|
||||
{key,value} in the matchLabels map is equivalent to an element
|
||||
of matchExpressions, whose key field is "key", the operator
|
||||
is "In", and the values array contains only "value". The requirements
|
||||
are ANDed.
|
||||
description: |-
|
||||
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
|
||||
map is equivalent to an element of matchExpressions, whose key field is "key", the
|
||||
operator is "In", and the values array contains only "value". The requirements are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
podSelector:
|
||||
description: |-
|
||||
PodSelector selects the pods that are targeted by this
|
||||
CRD.
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector requirements.
|
||||
The requirements are ANDed.
|
||||
items:
|
||||
description: |-
|
||||
A label selector requirement is a selector that contains values, a key, and an operator that
|
||||
relates the key and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector applies
|
||||
to.
|
||||
type: string
|
||||
operator:
|
||||
description: |-
|
||||
operator represents a key's relationship to a set of values.
|
||||
Valid operators are In, NotIn, Exists and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: |-
|
||||
values is an array of string values. If the operator is In or NotIn,
|
||||
the values array must be non-empty. If the operator is Exists or DoesNotExist,
|
||||
the values array must be empty. This array is replaced during a strategic
|
||||
merge patch.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- key
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: |-
|
||||
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
|
||||
map is equivalent to an element of matchExpressions, whose key field is "key", the
|
||||
operator is "In", and the values array contains only "value". The requirements are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
spiffeIDTemplate:
|
||||
description: SPIFFEID is the SPIFFE ID template. The node and pod
|
||||
spec are made available to the template under .NodeSpec, .PodSpec
|
||||
respectively.
|
||||
description: |-
|
||||
SPIFFEID is the SPIFFE ID template. The node and pod spec are made
|
||||
available to the template under .NodeSpec, .PodSpec respectively.
|
||||
type: string
|
||||
ttl:
|
||||
description: TTL indicates an upper-bound time-to-live for SVIDs minted
|
||||
for this ClusterSPIFFEID. If unset, a default will be chosen.
|
||||
description: |-
|
||||
TTL indicates an upper-bound time-to-live for X509 SVIDs minted for this
|
||||
ClusterSPIFFEID. If unset, a default will be chosen.
|
||||
type: string
|
||||
workloadSelectorTemplates:
|
||||
description: WorkloadSelectorTemplates are templates to produce arbitrary
|
||||
workload selectors that apply to a given workload before it will
|
||||
receive this SPIFFE ID. The rendered value is interpreted by SPIRE
|
||||
and are of the form type:value, where the value may, and often does,
|
||||
contain semicolons, .e.g., k8s:container-image:docker/hello-world
|
||||
The node and pod spec are made available to the template under .NodeSpec,
|
||||
.PodSpec respectively.
|
||||
description: |-
|
||||
WorkloadSelectorTemplates are templates to produce arbitrary workload
|
||||
selectors that apply to a given workload before it will receive this
|
||||
SPIFFE ID. The rendered value is interpreted by SPIRE and are of the
|
||||
form type:value, where the value may, and often does, contain
|
||||
semicolons, .e.g., k8s:container-image:docker/hello-world
|
||||
The node and pod spec are made available to the template under
|
||||
.NodeSpec, .PodSpec respectively.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
|
@ -181,21 +210,22 @@ spec:
|
|||
description: Stats produced by the last entry reconciliation run
|
||||
properties:
|
||||
entriesMasked:
|
||||
description: How many entries were masked by entries for other
|
||||
ClusterSPIFFEIDs. This happens when one or more ClusterSPIFFEIDs
|
||||
produce an entry for the same pod with the same set of workload
|
||||
selectors.
|
||||
description: |-
|
||||
How many entries were masked by entries for other ClusterSPIFFEIDs.
|
||||
This happens when one or more ClusterSPIFFEIDs produce an entry for
|
||||
the same pod with the same set of workload selectors.
|
||||
type: integer
|
||||
entriesToSet:
|
||||
description: How many entries are to be set for this ClusterSPIFFEID.
|
||||
In nominal conditions, this should reflect the number of pods
|
||||
selected, but not always if there were problems encountered
|
||||
rendering an entry for the pod (RenderFailures) or entries are
|
||||
masked (EntriesMasked).
|
||||
description: |-
|
||||
How many entries are to be set for this ClusterSPIFFEID. In nominal
|
||||
conditions, this should reflect the number of pods selected, but not
|
||||
always if there were problems encountered rendering an entry for the pod
|
||||
(RenderFailures) or entries are masked (EntriesMasked).
|
||||
type: integer
|
||||
entryFailures:
|
||||
description: How many entries were unable to be set due to failures
|
||||
to create or update the entries via the SPIRE Server API.
|
||||
description: |-
|
||||
How many entries were unable to be set due to failures to create or
|
||||
update the entries via the SPIRE Server API.
|
||||
type: integer
|
||||
namespacesIgnored:
|
||||
description: How many (selected) namespaces were ignored (based
|
||||
|
@ -205,10 +235,11 @@ spec:
|
|||
description: How many namespaces were selected.
|
||||
type: integer
|
||||
podEntryRenderFailures:
|
||||
description: How many failures were encountered rendering an entry
|
||||
selected pods. This could be due to either a bad template in
|
||||
the ClusterSPIFFEID or Pod metadata that when applied to the
|
||||
template did not produce valid entry values.
|
||||
description: |-
|
||||
How many failures were encountered rendering an entry selected pods.
|
||||
This could be due to either a bad template in the ClusterSPIFFEID or
|
||||
Pod metadata that when applied to the template did not produce valid
|
||||
entry values.
|
||||
type: integer
|
||||
podsSelected:
|
||||
description: How many pods were selected out of the namespaces.
|
||||
|
|
|
@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
|||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.11.1
|
||||
creationTimestamp: null
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
name: clusterstaticentries.spire.spiffe.io
|
||||
spec:
|
||||
group: spire.spiffe.io
|
||||
|
@ -38,6 +37,9 @@ spec:
|
|||
properties:
|
||||
admin:
|
||||
type: boolean
|
||||
className:
|
||||
description: Set which Controller Class will act on this object
|
||||
type: string
|
||||
dnsNames:
|
||||
items:
|
||||
type: string
|
||||
|
@ -60,6 +62,8 @@ spec:
|
|||
type: array
|
||||
spiffeID:
|
||||
type: string
|
||||
storeSVID:
|
||||
type: boolean
|
||||
x509SVIDTTL:
|
||||
type: string
|
||||
required:
|
||||
|
@ -89,3 +93,9 @@ spec:
|
|||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
|
|
|
@ -103,7 +103,7 @@ spec:
|
|||
serviceAccountName: spire-agent
|
||||
containers:
|
||||
- name: spire-agent
|
||||
image: ghcr.io/spiffe/spire-agent:1.7.0
|
||||
image: ghcr.io/spiffe/spire-agent:1.10.4
|
||||
imagePullPolicy: IfNotPresent
|
||||
args: ["-config", "/run/spire/config/agent.conf"]
|
||||
env:
|
||||
|
@ -124,7 +124,7 @@ spec:
|
|||
mountPath: /run/spire/sockets
|
||||
# This is the container which runs the SPIFFE CSI driver.
|
||||
- name: spiffe-csi-driver
|
||||
image: ghcr.io/spiffe/spiffe-csi-driver:0.2.3
|
||||
image: ghcr.io/spiffe/spiffe-csi-driver:0.2.6
|
||||
imagePullPolicy: IfNotPresent
|
||||
args: [
|
||||
"-workload-api-socket-dir", "/spire-agent-socket",
|
||||
|
@ -157,7 +157,7 @@ spec:
|
|||
# of all the little details required to register a CSI driver with
|
||||
# the kubelet.
|
||||
- name: node-driver-registrar
|
||||
image: quay.io/k8scsi/csi-node-driver-registrar:v2.0.1
|
||||
image: registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.12.0
|
||||
imagePullPolicy: IfNotPresent
|
||||
args: [
|
||||
"-csi-address", "/spiffe-csi/csi.sock",
|
||||
|
|
|
@ -3,12 +3,13 @@ kind: ControllerManagerConfig
|
|||
metrics:
|
||||
bindAddress: 127.0.0.1:8082
|
||||
health:
|
||||
healthProbeBindAddress: 127.0.0.1:8083
|
||||
healthProbeBindAddress: 0.0.0.0:8083
|
||||
leaderElection:
|
||||
leaderElect: true
|
||||
resourceName: 98c9c988.spiffe.io
|
||||
resourceNamespace: spire-system
|
||||
clusterName: cluster1
|
||||
logLevel: info
|
||||
trustDomain: cluster1.demo
|
||||
ignoreNamespaces:
|
||||
- kube-system
|
||||
|
|
|
@ -176,7 +176,7 @@ spec:
|
|||
shareProcessNamespace: true
|
||||
containers:
|
||||
- name: spire-server
|
||||
image: ghcr.io/spiffe/spire-server:1.7.0
|
||||
image: ghcr.io/spiffe/spire-server:1.10.4
|
||||
imagePullPolicy: IfNotPresent
|
||||
args: ["-config", "/run/spire/server/config/server.conf"]
|
||||
ports:
|
||||
|
@ -192,6 +192,12 @@ spec:
|
|||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 9443
|
||||
- containerPort: 8083
|
||||
name: healthz
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /readyz
|
||||
port: healthz
|
||||
args:
|
||||
- "--config=spire-controller-manager-config.yaml"
|
||||
volumeMounts:
|
||||
|
|
|
@ -15,6 +15,9 @@ rules:
|
|||
- apiGroups: [""]
|
||||
resources: ["pods"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: [""]
|
||||
resources: ["endpoints"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: ["spire.spiffe.io"]
|
||||
resources: ["clusterfederatedtrustdomains"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||
|
|
|
@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
|||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.8.0
|
||||
creationTimestamp: null
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
name: clusterfederatedtrustdomains.spire.spiffe.io
|
||||
spec:
|
||||
group: spire.spiffe.io
|
||||
|
@ -64,6 +63,9 @@ spec:
|
|||
description: BundleEndpointURL is the URL of the bundle endpoint.
|
||||
It must be an HTTPS URL and cannot contain userinfo (i.e. username/password).
|
||||
type: string
|
||||
className:
|
||||
description: Set which Controller Class will act on this object
|
||||
type: string
|
||||
trustDomain:
|
||||
description: TrustDomain is the name of the trust domain to federate
|
||||
with (e.g. example.org)
|
||||
|
|
|
@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
|||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.8.0
|
||||
creationTimestamp: null
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
name: clusterspiffeids.spire.spiffe.io
|
||||
spec:
|
||||
group: spire.spiffe.io
|
||||
|
@ -21,14 +20,19 @@ spec:
|
|||
description: ClusterSPIFFEID is the Schema for the clusterspiffeids API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
|
@ -36,102 +40,79 @@ spec:
|
|||
description: ClusterSPIFFEIDSpec defines the desired state of ClusterSPIFFEID
|
||||
properties:
|
||||
admin:
|
||||
description: Admin indicates whether or not the SVID can be used to
|
||||
access the SPIRE administrative APIs. Extra care should be taken
|
||||
to only apply this SPIFFE ID to admin workloads.
|
||||
description: |-
|
||||
Admin indicates whether or not the SVID can be used to access the SPIRE
|
||||
administrative APIs. Extra care should be taken to only apply this
|
||||
SPIFFE ID to admin workloads.
|
||||
type: boolean
|
||||
autoPopulateDNSNames:
|
||||
description: AutoPopulateDNSNames indicates whether or not to auto
|
||||
populate service DNS names.
|
||||
type: boolean
|
||||
className:
|
||||
description: Set which Controller Class will act on this object
|
||||
type: string
|
||||
fallback:
|
||||
description: |-
|
||||
Apply this ID only if there are no other matching non fallback
|
||||
ClusterSPIFFEIDs
|
||||
type: boolean
|
||||
dnsNameTemplates:
|
||||
description: DNSNameTemplate represents templates for extra DNS names
|
||||
that are applicable to SVIDs minted for this ClusterSPIFFEID. The
|
||||
node and pod spec are made available to the template under .NodeSpec,
|
||||
.PodSpec respectively.
|
||||
description: |-
|
||||
DNSNameTemplate represents templates for extra DNS names that are
|
||||
applicable to SVIDs minted for this ClusterSPIFFEID.
|
||||
The node and pod spec are made available to the template under
|
||||
.NodeSpec, .PodSpec respectively.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
downstream:
|
||||
description: Downstream indicates that the entry describes a downstream SPIRE server.
|
||||
description: Downstream indicates that the entry describes a downstream
|
||||
SPIRE server.
|
||||
type: boolean
|
||||
federatesWith:
|
||||
description: FederatesWith is a list of trust domain names that workloads
|
||||
that obtain this SPIFFE ID will federate with.
|
||||
description: |-
|
||||
FederatesWith is a list of trust domain names that workloads that
|
||||
obtain this SPIFFE ID will federate with.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
hint:
|
||||
description: |-
|
||||
Set the entry hint
|
||||
type: string
|
||||
jwtTtl:
|
||||
description: JWTTTL indicates an upper-bound time-to-live for JWT
|
||||
SVIDs minted for this ClusterSPIFFEID.
|
||||
description: |-
|
||||
JWTTTL indicates an upper-bound time-to-live for JWT SVIDs minted for this
|
||||
ClusterSPIFFEID.
|
||||
type: string
|
||||
namespaceSelector:
|
||||
description: NamespaceSelector selects the namespaces that are targetted
|
||||
by this CRD.
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector requirements.
|
||||
The requirements are ANDed.
|
||||
items:
|
||||
description: A label selector requirement is a selector that
|
||||
contains values, a key, and an operator that relates the key
|
||||
and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector applies
|
||||
to.
|
||||
type: string
|
||||
operator:
|
||||
description: operator represents a key's relationship to
|
||||
a set of values. Valid operators are In, NotIn, Exists
|
||||
and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: values is an array of string values. If the
|
||||
operator is In or NotIn, the values array must be non-empty.
|
||||
If the operator is Exists or DoesNotExist, the values
|
||||
array must be empty. This array is replaced during a strategic
|
||||
merge patch.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- key
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: matchLabels is a map of {key,value} pairs. A single
|
||||
{key,value} in the matchLabels map is equivalent to an element
|
||||
of matchExpressions, whose key field is "key", the operator
|
||||
is "In", and the values array contains only "value". The requirements
|
||||
are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
podSelector:
|
||||
description: PodSelector selects the pods that are targetted by this
|
||||
description: |-
|
||||
NamespaceSelector selects the namespaces that are targeted by this
|
||||
CRD.
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector requirements.
|
||||
The requirements are ANDed.
|
||||
items:
|
||||
description: A label selector requirement is a selector that
|
||||
contains values, a key, and an operator that relates the key
|
||||
and values.
|
||||
description: |-
|
||||
A label selector requirement is a selector that contains values, a key, and an operator that
|
||||
relates the key and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector applies
|
||||
to.
|
||||
type: string
|
||||
operator:
|
||||
description: operator represents a key's relationship to
|
||||
a set of values. Valid operators are In, NotIn, Exists
|
||||
and DoesNotExist.
|
||||
description: |-
|
||||
operator represents a key's relationship to a set of values.
|
||||
Valid operators are In, NotIn, Exists and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: values is an array of string values. If the
|
||||
operator is In or NotIn, the values array must be non-empty.
|
||||
If the operator is Exists or DoesNotExist, the values
|
||||
array must be empty. This array is replaced during a strategic
|
||||
description: |-
|
||||
values is an array of string values. If the operator is In or NotIn,
|
||||
the values array must be non-empty. If the operator is Exists or DoesNotExist,
|
||||
the values array must be empty. This array is replaced during a strategic
|
||||
merge patch.
|
||||
items:
|
||||
type: string
|
||||
|
@ -144,30 +125,78 @@ spec:
|
|||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: matchLabels is a map of {key,value} pairs. A single
|
||||
{key,value} in the matchLabels map is equivalent to an element
|
||||
of matchExpressions, whose key field is "key", the operator
|
||||
is "In", and the values array contains only "value". The requirements
|
||||
are ANDed.
|
||||
description: |-
|
||||
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
|
||||
map is equivalent to an element of matchExpressions, whose key field is "key", the
|
||||
operator is "In", and the values array contains only "value". The requirements are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
podSelector:
|
||||
description: |-
|
||||
PodSelector selects the pods that are targeted by this
|
||||
CRD.
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector requirements.
|
||||
The requirements are ANDed.
|
||||
items:
|
||||
description: |-
|
||||
A label selector requirement is a selector that contains values, a key, and an operator that
|
||||
relates the key and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector applies
|
||||
to.
|
||||
type: string
|
||||
operator:
|
||||
description: |-
|
||||
operator represents a key's relationship to a set of values.
|
||||
Valid operators are In, NotIn, Exists and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: |-
|
||||
values is an array of string values. If the operator is In or NotIn,
|
||||
the values array must be non-empty. If the operator is Exists or DoesNotExist,
|
||||
the values array must be empty. This array is replaced during a strategic
|
||||
merge patch.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- key
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: |-
|
||||
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
|
||||
map is equivalent to an element of matchExpressions, whose key field is "key", the
|
||||
operator is "In", and the values array contains only "value". The requirements are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
spiffeIDTemplate:
|
||||
description: SPIFFEID is the SPIFFE ID template. The node and pod
|
||||
spec are made available to the template under .NodeSpec, .PodSpec
|
||||
respectively.
|
||||
description: |-
|
||||
SPIFFEID is the SPIFFE ID template. The node and pod spec are made
|
||||
available to the template under .NodeSpec, .PodSpec respectively.
|
||||
type: string
|
||||
ttl:
|
||||
description: TTL indicates an upper-bound time-to-live for SVIDs minted
|
||||
for this ClusterSPIFFEID. If unset, a default will be chosen.
|
||||
description: |-
|
||||
TTL indicates an upper-bound time-to-live for X509 SVIDs minted for this
|
||||
ClusterSPIFFEID. If unset, a default will be chosen.
|
||||
type: string
|
||||
workloadSelectorTemplates:
|
||||
description: WorkloadSelectorTemplates are templates to produce arbitrary
|
||||
workload selectors that apply to a given workload before it will
|
||||
receive this SPIFFE ID. The rendered value is interpreted by SPIRE
|
||||
and are of the form type:value, where the value may, and often does,
|
||||
contain semicolons, .e.g., k8s:container-image:docker/hello-world
|
||||
The node and pod spec are made available to the template under .NodeSpec,
|
||||
.PodSpec respectively.
|
||||
description: |-
|
||||
WorkloadSelectorTemplates are templates to produce arbitrary workload
|
||||
selectors that apply to a given workload before it will receive this
|
||||
SPIFFE ID. The rendered value is interpreted by SPIRE and are of the
|
||||
form type:value, where the value may, and often does, contain
|
||||
semicolons, .e.g., k8s:container-image:docker/hello-world
|
||||
The node and pod spec are made available to the template under
|
||||
.NodeSpec, .PodSpec respectively.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
|
@ -181,21 +210,22 @@ spec:
|
|||
description: Stats produced by the last entry reconciliation run
|
||||
properties:
|
||||
entriesMasked:
|
||||
description: How many entries were masked by entries for other
|
||||
ClusterSPIFFEIDs. This happens when one or more ClusterSPIFFEIDs
|
||||
produce an entry for the same pod with the same set of workload
|
||||
selectors.
|
||||
description: |-
|
||||
How many entries were masked by entries for other ClusterSPIFFEIDs.
|
||||
This happens when one or more ClusterSPIFFEIDs produce an entry for
|
||||
the same pod with the same set of workload selectors.
|
||||
type: integer
|
||||
entriesToSet:
|
||||
description: How many entries are to be set for this ClusterSPIFFEID.
|
||||
In nominal conditions, this should reflect the number of pods
|
||||
selected, but not always if there were problems encountered
|
||||
rendering an entry for the pod (RenderFailures) or entries are
|
||||
masked (EntriesMasked).
|
||||
description: |-
|
||||
How many entries are to be set for this ClusterSPIFFEID. In nominal
|
||||
conditions, this should reflect the number of pods selected, but not
|
||||
always if there were problems encountered rendering an entry for the pod
|
||||
(RenderFailures) or entries are masked (EntriesMasked).
|
||||
type: integer
|
||||
entryFailures:
|
||||
description: How many entries were unable to be set due to failures
|
||||
to create or update the entries via the SPIRE Server API.
|
||||
description: |-
|
||||
How many entries were unable to be set due to failures to create or
|
||||
update the entries via the SPIRE Server API.
|
||||
type: integer
|
||||
namespacesIgnored:
|
||||
description: How many (selected) namespaces were ignored (based
|
||||
|
@ -205,10 +235,11 @@ spec:
|
|||
description: How many namespaces were selected.
|
||||
type: integer
|
||||
podEntryRenderFailures:
|
||||
description: How many failures were encountered rendering an entry
|
||||
selected pods. This could be due to either a bad template in
|
||||
the ClusterSPIFFEID or Pod metadata that when applied to the
|
||||
template did not produce valid entry values.
|
||||
description: |-
|
||||
How many failures were encountered rendering an entry selected pods.
|
||||
This could be due to either a bad template in the ClusterSPIFFEID or
|
||||
Pod metadata that when applied to the template did not produce valid
|
||||
entry values.
|
||||
type: integer
|
||||
podsSelected:
|
||||
description: How many pods were selected out of the namespaces.
|
||||
|
|
|
@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
|||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.11.1
|
||||
creationTimestamp: null
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
name: clusterstaticentries.spire.spiffe.io
|
||||
spec:
|
||||
group: spire.spiffe.io
|
||||
|
@ -38,6 +37,9 @@ spec:
|
|||
properties:
|
||||
admin:
|
||||
type: boolean
|
||||
className:
|
||||
description: Set which Controller Class will act on this object
|
||||
type: string
|
||||
dnsNames:
|
||||
items:
|
||||
type: string
|
||||
|
@ -60,6 +62,8 @@ spec:
|
|||
type: array
|
||||
spiffeID:
|
||||
type: string
|
||||
storeSVID:
|
||||
type: boolean
|
||||
x509SVIDTTL:
|
||||
type: string
|
||||
required:
|
||||
|
@ -89,3 +93,9 @@ spec:
|
|||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
|
|
|
@ -103,7 +103,7 @@ spec:
|
|||
serviceAccountName: spire-agent
|
||||
containers:
|
||||
- name: spire-agent
|
||||
image: ghcr.io/spiffe/spire-agent:1.7.0
|
||||
image: ghcr.io/spiffe/spire-agent:1.10.4
|
||||
imagePullPolicy: IfNotPresent
|
||||
args: ["-config", "/run/spire/config/agent.conf"]
|
||||
env:
|
||||
|
@ -124,7 +124,7 @@ spec:
|
|||
mountPath: /run/spire/sockets
|
||||
# This is the container which runs the SPIFFE CSI driver.
|
||||
- name: spiffe-csi-driver
|
||||
image: ghcr.io/spiffe/spiffe-csi-driver:0.2.3
|
||||
image: ghcr.io/spiffe/spiffe-csi-driver:0.2.6
|
||||
imagePullPolicy: IfNotPresent
|
||||
args: [
|
||||
"-workload-api-socket-dir", "/spire-agent-socket",
|
||||
|
@ -157,7 +157,7 @@ spec:
|
|||
# of all the little details required to register a CSI driver with
|
||||
# the kubelet.
|
||||
- name: node-driver-registrar
|
||||
image: quay.io/k8scsi/csi-node-driver-registrar:v2.0.1
|
||||
image: registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.12.0
|
||||
imagePullPolicy: IfNotPresent
|
||||
args: [
|
||||
"-csi-address", "/spiffe-csi/csi.sock",
|
||||
|
|
|
@ -3,12 +3,13 @@ kind: ControllerManagerConfig
|
|||
metrics:
|
||||
bindAddress: 127.0.0.1:8082
|
||||
health:
|
||||
healthProbeBindAddress: 127.0.0.1:8083
|
||||
healthProbeBindAddress: 0.0.0.0:8083
|
||||
leaderElection:
|
||||
leaderElect: true
|
||||
resourceName: 98c9c988.spiffe.io
|
||||
resourceNamespace: spire-system
|
||||
clusterName: cluster2
|
||||
logLevel: info
|
||||
trustDomain: cluster2.demo
|
||||
ignoreNamespaces:
|
||||
- kube-system
|
||||
|
|
|
@ -176,7 +176,7 @@ spec:
|
|||
shareProcessNamespace: true
|
||||
containers:
|
||||
- name: spire-server
|
||||
image: ghcr.io/spiffe/spire-server:1.7.0
|
||||
image: ghcr.io/spiffe/spire-server:1.10.4
|
||||
imagePullPolicy: IfNotPresent
|
||||
args: ["-config", "/run/spire/server/config/server.conf"]
|
||||
ports:
|
||||
|
@ -192,6 +192,12 @@ spec:
|
|||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 9443
|
||||
- containerPort: 8083
|
||||
name: healthz
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /readyz
|
||||
port: healthz
|
||||
args:
|
||||
- "--config=spire-controller-manager-config.yaml"
|
||||
volumeMounts:
|
||||
|
|
|
@ -13,3 +13,4 @@ spec:
|
|||
hint: "static-hint-2"
|
||||
admin: true
|
||||
downstream: true
|
||||
storeSVID: true
|
|
@ -1,4 +1,4 @@
|
|||
FROM golang:1.20.1-alpine AS builder
|
||||
FROM golang:1.23.4-alpine AS builder
|
||||
WORKDIR /workspace
|
||||
COPY go.mod go.mod
|
||||
COPY go.sum go.sum
|
||||
|
|
|
@ -43,7 +43,7 @@ func main() {
|
|||
|
||||
creds := grpccredentials.MTLSClientCredentials(source, source, tlsconfig.AuthorizeID(serverID))
|
||||
|
||||
client, err := grpc.DialContext(ctx, addr, grpc.WithTransportCredentials(creds))
|
||||
client, err := grpc.NewClient(addr, grpc.WithTransportCredentials(creds))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -1,24 +1,21 @@
|
|||
module greeter
|
||||
|
||||
go 1.20
|
||||
go 1.23.4
|
||||
|
||||
require (
|
||||
github.com/spiffe/go-spiffe/v2 v2.1.2
|
||||
google.golang.org/grpc v1.53.0
|
||||
google.golang.org/grpc/examples v0.0.0-20230224211313-3775f633ce20
|
||||
github.com/spiffe/go-spiffe/v2 v2.5.0
|
||||
google.golang.org/grpc v1.73.0
|
||||
google.golang.org/grpc/examples v0.0.0-20240422202308-34de5cf4832f
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Microsoft/go-winio v0.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/zeebo/errs v1.3.0 // indirect
|
||||
golang.org/x/crypto v0.6.0 // indirect
|
||||
golang.org/x/mod v0.8.0 // indirect
|
||||
golang.org/x/net v0.7.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
golang.org/x/tools v0.6.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230223222841-637eb2293923 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
||||
github.com/zeebo/errs v1.4.0 // indirect
|
||||
golang.org/x/crypto v0.36.0 // indirect
|
||||
golang.org/x/net v0.38.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
)
|
||||
|
|
|
@ -1,41 +1,54 @@
|
|||
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
|
||||
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/spiffe/go-spiffe/v2 v2.1.2 h1:nfNwopOP7q0qsWU6AUASqmbtYViwHA6vuHyAtqFJtNc=
|
||||
github.com/spiffe/go-spiffe/v2 v2.1.2/go.mod h1:cbQmFrxsOpbm5tWURAYip9ZK0dOSFeoFG3/5Ub9Hvy0=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/zeebo/errs v1.3.0 h1:hmiaKqgYZzcVgRL1Vkc1Mn2914BbzB0IBxs+ebeutGs=
|
||||
github.com/zeebo/errs v1.3.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
|
||||
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto v0.0.0-20230223222841-637eb2293923 h1:znp6mq/drrY+6khTAlJUDNFFcDGV2ENLYKpMq8SyCds=
|
||||
google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw=
|
||||
google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=
|
||||
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
|
||||
google.golang.org/grpc/examples v0.0.0-20230224211313-3775f633ce20 h1:MLBCGN1O7GzIx+cBiwfYPwtmZ41U3Mn/cotLJciaArI=
|
||||
google.golang.org/grpc/examples v0.0.0-20230224211313-3775f633ce20/go.mod h1:Nr5H8+MlGWr5+xX/STzdoEqJrO+YteqFbMyCsrb6mH0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
|
||||
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE=
|
||||
github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM=
|
||||
github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/grpc/examples v0.0.0-20240422202308-34de5cf4832f h1:DXDiMO+e57lNmXq6CXCWgoiLMvTWyJpmm8q1xQB4cFM=
|
||||
google.golang.org/grpc/examples v0.0.0-20240422202308-34de5cf4832f/go.mod h1:uaPEAc5V00jjG3DPhGFLXGT290RUV3+aNQigs1W50/8=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
set -eo pipefail
|
||||
|
||||
kubectl exec -t \
|
||||
-nspire-system \
|
||||
-n spire-system \
|
||||
-c spire-server deployment/spire-server -- \
|
||||
/opt/spire/bin/spire-server entry show \
|
||||
"$@"
|
||||
|
|
|
@ -3,6 +3,6 @@
|
|||
set -eo pipefail
|
||||
|
||||
kubectl exec -t \
|
||||
-nspire-system \
|
||||
-n spire-system \
|
||||
-c spire-server deployment/spire-server -- \
|
||||
/opt/spire/bin/spire-server bundle list -format spiffe
|
||||
|
|
50
demo/test.sh
50
demo/test.sh
|
@ -31,13 +31,40 @@ DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
|||
cd "$DIR"
|
||||
|
||||
cleanup() {
|
||||
if [[ "$1" -ne 0 ]]; then
|
||||
cat <<EOF >>"$GITHUB_STEP_SUMMARY"
|
||||
### Describe Pods Cluster 1
|
||||
\`\`\`
|
||||
$(./cluster1 kubectl describe pods -n "spire-system")
|
||||
\`\`\`
|
||||
|
||||
### Logs Cluster 1
|
||||
|
||||
\`\`\`
|
||||
$(./cluster1 kubectl get pods -o name -n "spire-system" | while read -r line; do echo; echo "logs for ${line}:"; ./cluster1 kubectl logs -n "spire-system" "${line}" --prefix --all-containers=true --ignore-errors=true; done)
|
||||
\`\`\`
|
||||
|
||||
### Describe Pods Cluster 2
|
||||
|
||||
\`\`\`
|
||||
$(./cluster2 kubectl describe pods -n "spire-system")
|
||||
\`\`\`
|
||||
|
||||
### Logs Cluster 2
|
||||
|
||||
\`\`\`
|
||||
$(./cluster2 kubectl get pods -o name -n "spire-system" | while read -r line; do echo; echo logs for "${line}:"; ./cluster2 kubectl logs -n "spire-system" "${line}" --prefix --all-containers=true --ignore-errors=true; done)
|
||||
\`\`\`
|
||||
EOF
|
||||
fi
|
||||
|
||||
echo "Cleaning up..."
|
||||
./cluster1 kind delete cluster || true
|
||||
./cluster2 kind delete cluster || true
|
||||
echo "Done."
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
trap 'EC=$? && trap - SIGTERM && cleanup $EC' SIGINT SIGTERM EXIT
|
||||
|
||||
log-info "Tagging devel image as nightly..."
|
||||
docker tag ghcr.io/spiffe/spire-controller-manager:{devel,nightly}
|
||||
|
@ -46,9 +73,10 @@ log-info "Building greeter server/client..."
|
|||
(cd greeter; make docker-build)
|
||||
|
||||
log-info "Pulling docker images..."
|
||||
echo ghcr.io/spiffe/spire-server:1.7.0 \
|
||||
ghcr.io/spiffe/spire-agent:1.7.0 \
|
||||
ghcr.io/spiffe/spiffe-csi-driver:0.2.0 \
|
||||
echo ghcr.io/spiffe/spire-server:1.10.4 \
|
||||
ghcr.io/spiffe/spire-agent:1.10.4 \
|
||||
ghcr.io/spiffe/spiffe-csi-driver:0.2.6 \
|
||||
registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.12.0 \
|
||||
| xargs -n1 docker pull
|
||||
|
||||
log-info "Creating cluster1..."
|
||||
|
@ -59,18 +87,20 @@ log-info "Creating cluster2..."
|
|||
|
||||
log-info "Loading images into cluster1..."
|
||||
echo \
|
||||
ghcr.io/spiffe/spire-server:1.7.0 \
|
||||
ghcr.io/spiffe/spire-agent:1.7.0 \
|
||||
ghcr.io/spiffe/spiffe-csi-driver:0.2.0 \
|
||||
ghcr.io/spiffe/spire-server:1.10.4 \
|
||||
ghcr.io/spiffe/spire-agent:1.10.4 \
|
||||
ghcr.io/spiffe/spiffe-csi-driver:0.2.6 \
|
||||
registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.12.0 \
|
||||
ghcr.io/spiffe/spire-controller-manager:nightly \
|
||||
greeter-server:demo \
|
||||
| xargs -n1 ./cluster1 kind load docker-image
|
||||
|
||||
log-info "Loading images into cluster2..."
|
||||
echo \
|
||||
ghcr.io/spiffe/spire-server:1.7.0 \
|
||||
ghcr.io/spiffe/spire-agent:1.7.0 \
|
||||
ghcr.io/spiffe/spiffe-csi-driver:0.2.0 \
|
||||
ghcr.io/spiffe/spire-server:1.10.4 \
|
||||
ghcr.io/spiffe/spire-agent:1.10.4 \
|
||||
ghcr.io/spiffe/spiffe-csi-driver:0.2.6 \
|
||||
registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.12.0 \
|
||||
ghcr.io/spiffe/spire-controller-manager:nightly \
|
||||
greeter-client:demo \
|
||||
| xargs -n1 ./cluster2 kind load docker-image
|
||||
|
|
|
@ -16,6 +16,7 @@ See the [SPIFFE Federation](https://github.com/spiffe/spiffe/blob/main/standards
|
|||
| `bundleEndpointURL` | REQUIRED | `https://somedomain.test/bundle` | An HTTPS URL to the bundle endpoint for the foreign trust domain. |
|
||||
| `bundleEndpointProfile` | REQUIRED | See [Bundle Endpoint Profile](#bundle-endpoint-profile) | The profile for the bundle endpoint for the foreign trust domain. |
|
||||
| `trustDomainBundle` | OPTIONAL | | The bundle contents for the foreign trust domain. |
|
||||
| `className` | OPTIONAL | | The class name of the SPIRE controller manager. |
|
||||
|
||||
### Bundle Endpoint Profile
|
||||
|
||||
|
|
|
@ -26,6 +26,9 @@ The definition can be found [here](../api/v1alpha1/clusterspiffeid_types.go).
|
|||
| `federatesWith` | OPTIONAL | One or more trust domain names that target workloads federate with |
|
||||
| `admin` | OPTIONAL | Indicates whether the target workload is an admin workload (i.e. can access SPIRE administrative APIs) |
|
||||
| `downstream` | OPTIONAL | Indicates that the entry describes a downstream SPIRE server. |
|
||||
| `autoPopulateDNSNames` | OPTIONAL | Indicates whether or not to auto populate service DNS names. |
|
||||
| `fallback` | OPTIONAL | Apply this ID only if there are no other matching non fallback ClusterSPIFFEIDs. |
|
||||
| `className` | OPTIONAL | The class name of the SPIRE controller manager. |
|
||||
|
||||
## ClusterSPIFFEIDStatus
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ The definition can be found [here](../api/v1alpha1/clusterstaticentry_types.go).
|
|||
| `hint` | OPTIONAL | An opaque string that is provided to the workload as a hint on how the SVID should be used |
|
||||
| `admin` | OPTIONAL | Indicates whether the target workload is an admin workload (i.e. can access SPIRE administrative APIs) |
|
||||
| `downstream` | OPTIONAL | Indicates that the entry describes a downstream SPIRE server. |
|
||||
| `storeSVID` | OPTIONAL | Indicates whether the issued SVID must be stored through an SVIDStore plugin. |
|
||||
| `className` | OPTIONAL | The class name of the SPIRE controller manager. |
|
||||
|
||||
## ClusterStaticEntryStatus
|
||||
|
||||
|
|
|
@ -2,14 +2,31 @@
|
|||
|
||||
The SPIRE Controller Manager configuration is defined [here](../api/v1alpha1/controllermanagerconfig_types.go).
|
||||
|
||||
Beyond the standard [controller manager configuration](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1#ControllerConfigurationSpec), the following fields are defined:
|
||||
Beyond the
|
||||
standard [controller manager configuration](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1#ControllerConfigurationSpec),
|
||||
the following fields are defined:
|
||||
|
||||
| Field | Required | Default | Description |
|
||||
| ------------------------------------ | -------- | ------------------------------------------------ | ------------------------------------------------------------------ |
|
||||
| `clusterName` | REQUIRED | | The name of the cluster |
|
||||
| `trustDomain` | REQUIRED | | The trust domain name for the cluster |
|
||||
| `clusterDomain` | OPTIONAL | | The domain of the cluster, ie `cluster.local`. If not specified will attempt to auto detect. |
|
||||
| `ignoreNamespaces` | OPTIONAL | `["kube-system", "kube-public", "spire-system"]` | Namespaces that the controllers should ignore |
|
||||
| `validatingWebhookConfigurationName` | OPTIONAL | `spire-controller-manager-webhook` | The name of the validating admission controller webhook to manage |
|
||||
| Field | Required | Default | Description |
|
||||
|--------------------------------------|----------|--------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `clusterName` | REQUIRED | | The name of the cluster |
|
||||
| `trustDomain` | REQUIRED | | The trust domain name for the cluster |
|
||||
| `clusterDomain` | OPTIONAL | | The domain of the cluster, ie `cluster.local`. If not specified will attempt to auto detect. |
|
||||
| `ignoreNamespaces` | OPTIONAL | `["kube-system", "kube-public", "spire-system"]` | Namespaces that the controllers should ignore |
|
||||
| `validatingWebhookConfigurationName` | OPTIONAL | `spire-controller-manager-webhook` | The name of the validating admission controller webhook to manage |
|
||||
| `gcInterval` | OPTIONAL | `10s` | How often the SPIRE state is reconciled when the controller is otherwise idle. This impacts how quickly SPIRE state will converge after CRDs are removed or SPIRE state is mutated underneath the controller. |
|
||||
| `spireServerSocketPath` | OPTIONAL | `/spire-server/api.sock` | The path the the SPIRE Server API socket |
|
||||
| `spireServerSocketPath` | OPTIONAL | `/spire-server/api.sock` | The path the the SPIRE Server API socket |
|
||||
| `logLevel` | OPTIONAL | `info` | The log level for the controller manager. Supported values are `info`, `error`, `warn` and `debug`. |
|
||||
| `logEncoding` | OPTIONAL | `console` | The log encoder for the controller manager. Supported values are `console` and `json`. |
|
||||
| `className` | OPTIONAL | | Only sync resources that have the specified className set on them. |
|
||||
| `watchClassless` | OPTIONAL | | If className is set, also watch for resources that do not have any className set. |
|
||||
| `staticManifestPath` | OPTIONAL | | If specified, manifests will be read from disk instead of from Kubernetes |
|
||||
|
||||
## Kubernetes Mode
|
||||
|
||||
By default, all objects are synced from the Kubernetes cluster the spire-controller-manager is running in.
|
||||
|
||||
## Static Mode
|
||||
|
||||
If `staticManifestPath` is specified, Kubernetes will not be used and instead, manifests are loaded from yaml files located in the specified path and synchronized to the SPIRE server.
|
||||
|
||||
In this mode, validating webhooks will be ignored as its not useful without Kubernetes.
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
apiVersion: spire.spiffe.io/v1alpha1
|
||||
kind: ControllerManagerConfig
|
||||
metadata:
|
||||
name: config
|
||||
metrics:
|
||||
bindAddress: 0.0.0.0:8082
|
||||
health:
|
||||
healthProbeBindAddress: 0.0.0.0:8083
|
||||
entryIDPrefix: scm
|
||||
className: scm
|
||||
clusterName: scm
|
||||
clusterDomain: local
|
||||
trustDomain: example.org
|
||||
watchClassless: true
|
||||
staticManifestPath: /etc/spire/server/main/manifests
|
||||
spireServerSocketPath: "/tmp/spire-server/private/api.sock"
|
||||
logLevel: info
|
118
go.mod
118
go.mod
|
@ -1,84 +1,84 @@
|
|||
module github.com/spiffe/spire-controller-manager
|
||||
|
||||
go 1.20
|
||||
go 1.23.4
|
||||
|
||||
require (
|
||||
github.com/go-logr/logr v1.2.4
|
||||
github.com/google/go-cmp v0.5.9
|
||||
github.com/go-logr/logr v1.4.2
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/jpillora/backoff v1.0.0
|
||||
github.com/onsi/ginkgo/v2 v2.12.0
|
||||
github.com/onsi/gomega v1.27.10
|
||||
github.com/spiffe/go-spiffe/v2 v2.1.6
|
||||
github.com/spiffe/spire-api-sdk v1.7.2
|
||||
github.com/stretchr/testify v1.8.4
|
||||
google.golang.org/grpc v1.58.0
|
||||
google.golang.org/protobuf v1.31.0
|
||||
k8s.io/api v0.28.1
|
||||
k8s.io/apimachinery v0.28.1
|
||||
k8s.io/client-go v0.28.1
|
||||
k8s.io/component-base v0.28.1
|
||||
k8s.io/utils v0.0.0-20230505201702-9f6742963106
|
||||
sigs.k8s.io/controller-runtime v0.15.1
|
||||
github.com/onsi/ginkgo/v2 v2.23.4
|
||||
github.com/onsi/gomega v1.37.0
|
||||
github.com/prometheus/client_golang v1.22.0
|
||||
github.com/spiffe/go-spiffe/v2 v2.5.0
|
||||
github.com/spiffe/spire-api-sdk v1.12.4
|
||||
github.com/stretchr/testify v1.10.0
|
||||
go.uber.org/zap v1.27.0
|
||||
google.golang.org/grpc v1.73.0
|
||||
google.golang.org/protobuf v1.36.6
|
||||
k8s.io/api v0.32.4
|
||||
k8s.io/apimachinery v0.32.4
|
||||
k8s.io/client-go v0.32.4
|
||||
k8s.io/component-base v0.32.4
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738
|
||||
sigs.k8s.io/controller-runtime v0.20.4
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.10.2 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.12.0 // indirect
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
|
||||
github.com/go-logr/zapr v1.2.4 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.22.3 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
||||
github.com/go-logr/zapr v1.3.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/imdario/mergo v0.3.16 // indirect
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.16.0 // indirect
|
||||
github.com/prometheus/client_model v0.4.0 // indirect
|
||||
github.com/prometheus/common v0.44.0 // indirect
|
||||
github.com/prometheus/procfs v0.10.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/zeebo/errs v1.3.0 // indirect
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/zeebo/errs v1.4.0 // indirect
|
||||
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.24.0 // indirect
|
||||
golang.org/x/crypto v0.12.0 // indirect
|
||||
golang.org/x/net v0.14.0 // indirect
|
||||
golang.org/x/oauth2 v0.10.0 // indirect
|
||||
golang.org/x/sys v0.11.0 // indirect
|
||||
golang.org/x/term v0.11.0 // indirect
|
||||
golang.org/x/text v0.12.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.12.0 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.3.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
|
||||
golang.org/x/crypto v0.36.0 // indirect
|
||||
golang.org/x/net v0.38.0 // indirect
|
||||
golang.org/x/oauth2 v0.28.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
golang.org/x/term v0.30.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
golang.org/x/time v0.7.0 // indirect
|
||||
golang.org/x/tools v0.31.0 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.27.2 // indirect
|
||||
k8s.io/klog/v2 v2.100.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.32.1 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
)
|
||||
|
|
298
go.sum
298
go.sum
|
@ -2,17 +2,12 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
|
|||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
|
@ -20,12 +15,12 @@ github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XP
|
|||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/emicklei/go-restful/v3 v3.10.2 h1:hIovbnmBTLjHXkqEBUz3HGpXZdM7ZrE9fJIZIqlJLqE=
|
||||
github.com/emicklei/go-restful/v3 v3.10.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/emicklei/go-restful/v3 v3.12.0 h1:y2DdzBAURM29NFF94q6RaY4vjIH1rtwDapwQtU84iWk=
|
||||
github.com/emicklei/go-restful/v3 v3.12.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
|
@ -34,34 +29,34 @@ github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.
|
|||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U=
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww=
|
||||
github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU=
|
||||
github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
|
||||
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo=
|
||||
github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA=
|
||||
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
|
||||
github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
||||
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
|
@ -74,8 +69,10 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
|
|||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
|
||||
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
|
@ -85,21 +82,18 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
|
||||
|
@ -108,17 +102,16 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
|
|||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
|
@ -126,123 +119,119 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
|||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/onsi/ginkgo/v2 v2.12.0 h1:UIVDowFPwpg6yMUpPjGkYvf06K3RAiJXUhCxEwQVHRI=
|
||||
github.com/onsi/ginkgo/v2 v2.12.0/go.mod h1:ZNEzXISYlqpb8S36iN71ifqLi3vVD1rVJGvWRCJOUpQ=
|
||||
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
|
||||
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
|
||||
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
|
||||
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
|
||||
github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
|
||||
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
||||
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
|
||||
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
|
||||
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
|
||||
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
|
||||
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
|
||||
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
||||
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spiffe/go-spiffe/v2 v2.1.6 h1:4SdizuQieFyL9eNU+SPiCArH4kynzaKOOj0VvM8R7Xo=
|
||||
github.com/spiffe/go-spiffe/v2 v2.1.6/go.mod h1:eVDqm9xFvyqao6C+eQensb9ZPkyNEeaUbqbBpOhBnNk=
|
||||
github.com/spiffe/spire-api-sdk v1.7.2 h1:F4rdMnEVBHZYxttXAEER5Rhs2Mm9ugn4S5j1tWpDHnQ=
|
||||
github.com/spiffe/spire-api-sdk v1.7.2/go.mod h1:4uuhFlN6KBWjACRP3xXwrOTNnvaLp1zJs8Lribtr4fI=
|
||||
github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE=
|
||||
github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g=
|
||||
github.com/spiffe/spire-api-sdk v1.12.4 h1:RFMW7aPylHrJOPWY+w+YjElKCRUJPOUAMEyn7w4wLTU=
|
||||
github.com/spiffe/spire-api-sdk v1.12.4/go.mod h1:4uuhFlN6KBWjACRP3xXwrOTNnvaLp1zJs8Lribtr4fI=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/zeebo/errs v1.3.0 h1:hmiaKqgYZzcVgRL1Vkc1Mn2914BbzB0IBxs+ebeutGs=
|
||||
github.com/zeebo/errs v1.3.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
|
||||
github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM=
|
||||
github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
|
||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
|
||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8=
|
||||
golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI=
|
||||
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
|
||||
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
|
||||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
|
||||
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
|
||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
|
@ -251,25 +240,22 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
|
|||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
|
||||
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
|
||||
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
|
||||
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gomodules.xyz/jsonpatch/v2 v2.3.0 h1:8NFhfS6gzxNqjLIYnZxg319wZ5Qjnx4m/CcX+Klzazc=
|
||||
gomodules.xyz/jsonpatch/v2 v2.3.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
|
@ -277,8 +263,8 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
|
|||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc v1.58.0 h1:32JY8YpPMSR45K+c3o6b8VL73V+rR8k+DeMIr4vRH8o=
|
||||
google.golang.org/grpc v1.58.0/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
|
@ -292,45 +278,43 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
|
|||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
k8s.io/api v0.28.1 h1:i+0O8k2NPBCPYaMB+uCkseEbawEt/eFaiRqUx8aB108=
|
||||
k8s.io/api v0.28.1/go.mod h1:uBYwID+66wiL28Kn2tBjBYQdEU0Xk0z5qF8bIBqk/Dg=
|
||||
k8s.io/apiextensions-apiserver v0.27.2 h1:iwhyoeS4xj9Y7v8YExhUwbVuBhMr3Q4bd/laClBV6Bo=
|
||||
k8s.io/apiextensions-apiserver v0.27.2/go.mod h1:Oz9UdvGguL3ULgRdY9QMUzL2RZImotgxvGjdWRq6ZXQ=
|
||||
k8s.io/apimachinery v0.28.1 h1:EJD40og3GizBSV3mkIoXQBsws32okPOy+MkRyzh6nPY=
|
||||
k8s.io/apimachinery v0.28.1/go.mod h1:X0xh/chESs2hP9koe+SdIAcXWcQ+RM5hy0ZynB+yEvw=
|
||||
k8s.io/client-go v0.28.1 h1:pRhMzB8HyLfVwpngWKE8hDcXRqifh1ga2Z/PU9SXVK8=
|
||||
k8s.io/client-go v0.28.1/go.mod h1:pEZA3FqOsVkCc07pFVzK076R+P/eXqsgx5zuuRWukNE=
|
||||
k8s.io/component-base v0.28.1 h1:LA4AujMlK2mr0tZbQDZkjWbdhTV5bRyEyAFe0TJxlWg=
|
||||
k8s.io/component-base v0.28.1/go.mod h1:jI11OyhbX21Qtbav7JkhehyBsIRfnO8oEgoAR12ArIU=
|
||||
k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg=
|
||||
k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
|
||||
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ=
|
||||
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM=
|
||||
k8s.io/utils v0.0.0-20230505201702-9f6742963106 h1:EObNQ3TW2D+WptiYXlApGNLVy0zm/JIBVY9i+M4wpAU=
|
||||
k8s.io/utils v0.0.0-20230505201702-9f6742963106/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/controller-runtime v0.15.1 h1:9UvgKD4ZJGcj24vefUFgZFP3xej/3igL9BsOUTb/+4c=
|
||||
sigs.k8s.io/controller-runtime v0.15.1/go.mod h1:7ngYvp1MLT+9GeZ+6lH3LOlcHkp/+tzA/fmHa4iq9kk=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
k8s.io/api v0.32.4 h1:kw8Y/G8E7EpNy7gjB8gJZl3KJkNz8HM2YHrZPtAZsF4=
|
||||
k8s.io/api v0.32.4/go.mod h1:5MYFvLvweRhyKylM3Es/6uh/5hGp0dg82vP34KifX4g=
|
||||
k8s.io/apiextensions-apiserver v0.32.1 h1:hjkALhRUeCariC8DiVmb5jj0VjIc1N0DREP32+6UXZw=
|
||||
k8s.io/apiextensions-apiserver v0.32.1/go.mod h1:sxWIGuGiYov7Io1fAS2X06NjMIk5CbRHc2StSmbaQto=
|
||||
k8s.io/apimachinery v0.32.4 h1:8EEksaxA7nd7xWJkkwLDN4SvWS5ot9g6Z/VZb3ju25I=
|
||||
k8s.io/apimachinery v0.32.4/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
|
||||
k8s.io/client-go v0.32.4 h1:zaGJS7xoYOYumoWIFXlcVrsiYioRPrXGO7dBfVC5R6M=
|
||||
k8s.io/client-go v0.32.4/go.mod h1:k0jftcyYnEtwlFW92xC7MTtFv5BNcZBr+zn9jPlT9Ic=
|
||||
k8s.io/component-base v0.32.4 h1:HuF+2JVLbFS5GODLIfPCb1Td6b+G2HszJoArcWOSr5I=
|
||||
k8s.io/component-base v0.32.4/go.mod h1:10KloJEYw1keU/Xmjfy9TKJqUq7J2mYdiD1VDXoco4o=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/controller-runtime v0.20.4 h1:X3c+Odnxz+iPTRobG4tp092+CvBU9UK0t/bRf+n0DGU=
|
||||
sigs.k8s.io/controller-runtime v0.20.4/go.mod h1:xg2XB0K5ShQzAgsoujxuKN4LNXR2LfwwHsPj7Iaw+XY=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -41,7 +41,7 @@ type ClusterFederatedTrustDomainReconciler struct {
|
|||
|
||||
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||
// move the current state of the cluster closer to the desired state.
|
||||
func (r *ClusterFederatedTrustDomainReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
func (r *ClusterFederatedTrustDomainReconciler) Reconcile(ctx context.Context, _ ctrl.Request) (ctrl.Result, error) {
|
||||
log.FromContext(ctx).V(1).Info("Triggering reconciliation")
|
||||
r.Triggerer.Trigger()
|
||||
return ctrl.Result{}, nil
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -44,7 +44,7 @@ type ClusterSPIFFEIDReconciler struct {
|
|||
|
||||
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||
// move the current state of the cluster closer to the desired state.
|
||||
func (r *ClusterSPIFFEIDReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Result, err error) {
|
||||
func (r *ClusterSPIFFEIDReconciler) Reconcile(ctx context.Context, _ ctrl.Request) (ctrl.Result, error) {
|
||||
log.FromContext(ctx).V(1).Info("Triggering reconciliation")
|
||||
r.Triggerer.Trigger()
|
||||
return ctrl.Result{}, nil
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -39,7 +39,9 @@ type ClusterStaticEntryReconciler struct {
|
|||
//+kubebuilder:rbac:groups=spire.spiffe.io,resources=clusterstaticentries/status,verbs=get;update;patch
|
||||
//+kubebuilder:rbac:groups=spire.spiffe.io,resources=clusterstaticentries/finalizers,verbs=update
|
||||
|
||||
func (r *ClusterStaticEntryReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||
// move the current state of the cluster closer to the desired state.
|
||||
func (r *ClusterStaticEntryReconciler) Reconcile(ctx context.Context, _ ctrl.Request) (ctrl.Result, error) {
|
||||
log.FromContext(ctx).V(1).Info("Triggering reconciliation")
|
||||
r.Triggerer.Trigger()
|
||||
return ctrl.Result{}, nil
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
package controller
|
||||
|
||||
type EntryReconciler interface {
|
||||
Trigger()
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2021 SPIRE Authors.
|
||||
Copyright 2023 SPIRE Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -14,16 +14,14 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"regexp"
|
||||
|
||||
"github.com/spiffe/spire-controller-manager/pkg/namespace"
|
||||
"github.com/spiffe/spire-controller-manager/pkg/reconciler"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
|
@ -31,8 +29,8 @@ import (
|
|||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
)
|
||||
|
||||
// PodReconciler reconciles a Pod object
|
||||
type PodReconciler struct {
|
||||
// EndpointReconciler reconciles a Pod object
|
||||
type EndpointsReconciler struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
Triggerer reconciler.Triggerer
|
||||
|
@ -45,10 +43,11 @@ type PodReconciler struct {
|
|||
//+kubebuilder:rbac:groups="",resources=namespaces,verbs=get;list;watch
|
||||
//+kubebuilder:rbac:groups="",resources=pods,verbs=get;list;watch
|
||||
//+kubebuilder:rbac:groups="",resources=nodes,verbs=get;list;watch
|
||||
//+kubebuilder:rbac:groups="",resources=endpoints,verbs=get;list;watch
|
||||
|
||||
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||
// move the current state of the cluster closer to the desired state.
|
||||
func (r *PodReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Result, err error) {
|
||||
func (r *EndpointsReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Result, err error) {
|
||||
if namespace.IsIgnored(r.IgnoreNamespaces, req.Namespace) {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
@ -60,8 +59,8 @@ func (r *PodReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl
|
|||
}
|
||||
|
||||
// SetupWithManager sets up the controller with the Manager.
|
||||
func (r *PodReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
func (r *EndpointsReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&corev1.Pod{}).
|
||||
For(&corev1.Endpoints{}).
|
||||
Complete(r)
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
Copyright 2021 SPIRE Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/spiffe/spire-controller-manager/pkg/namespace"
|
||||
"github.com/spiffe/spire-controller-manager/pkg/reconciler"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
)
|
||||
|
||||
// PodReconciler reconciles a Pod object
|
||||
type PodReconciler struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
Triggerer reconciler.Triggerer
|
||||
IgnoreNamespaces []*regexp.Regexp
|
||||
AutoPopulateDNSNames bool
|
||||
}
|
||||
|
||||
//+kubebuilder:rbac:groups=spire.spiffe.io,resources=clusterspiffeids,verbs=get;list;watch;create;update;patch;delete
|
||||
//+kubebuilder:rbac:groups=spire.spiffe.io,resources=clusterspiffeids/status,verbs=get;update;patch
|
||||
//+kubebuilder:rbac:groups=spire.spiffe.io,resources=clusterspiffeids/finalizers,verbs=update
|
||||
//+kubebuilder:rbac:groups="",resources=namespaces,verbs=get;list;watch
|
||||
//+kubebuilder:rbac:groups="",resources=pods,verbs=get;list;watch
|
||||
//+kubebuilder:rbac:groups="",resources=nodes,verbs=get;list;watch
|
||||
//+kubebuilder:rbac:groups="",resources=endpoints,verbs=get;list;watch
|
||||
// Required to patch webhook config with spire CA
|
||||
//+kubebuilder:rbac:groups="admissionregistration.k8s.io",resources=validatingwebhookconfigurations,verbs=get;list;patch;watch
|
||||
|
||||
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||
// move the current state of the cluster closer to the desired state.
|
||||
func (r *PodReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Result, err error) {
|
||||
if namespace.IsIgnored(r.IgnoreNamespaces, req.Namespace) {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
log.FromContext(ctx).V(1).Info("Triggering reconciliation")
|
||||
r.Triggerer.Trigger()
|
||||
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// SetupWithManager sets up the controller with the Manager.
|
||||
func (r *PodReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error {
|
||||
// Index endpoints by UID. Later when we reconcile the Pod this will make it easy to find the associated endpoints
|
||||
// and auto populate DNS names.
|
||||
err := mgr.GetFieldIndexer().IndexField(ctx, &corev1.Endpoints{}, reconciler.EndpointUID, func(rawObj client.Object) []string {
|
||||
endpoints, ok := rawObj.(*corev1.Endpoints)
|
||||
if !ok {
|
||||
log.FromContext(ctx).Error(nil, "unexpected type indexing fields", "type", fmt.Sprintf("%T", rawObj), "expecteed", "*corev1.Endpoints")
|
||||
return nil
|
||||
}
|
||||
var podUIDs []string
|
||||
for _, subset := range endpoints.Subsets {
|
||||
for _, address := range subset.Addresses {
|
||||
if address.TargetRef != nil && address.TargetRef.Kind == "Pod" {
|
||||
podUIDs = append(podUIDs, string(address.TargetRef.UID))
|
||||
}
|
||||
}
|
||||
for _, address := range subset.NotReadyAddresses {
|
||||
if address.TargetRef != nil && address.TargetRef.Kind == "Pod" {
|
||||
podUIDs = append(podUIDs, string(address.TargetRef.UID))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return podUIDs
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&corev1.Pod{}).
|
||||
Complete(r)
|
||||
}
|
|
@ -14,15 +14,19 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
. "github.com/onsi/ginkgo/v2" //nolint:revive // auto-generated
|
||||
. "github.com/onsi/gomega" //nolint:revive // auto-generated
|
||||
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
|
@ -35,10 +39,11 @@ import (
|
|||
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
|
||||
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
|
||||
|
||||
var cfg *rest.Config
|
||||
var k8sClient client.Client
|
||||
var testEnv *envtest.Environment
|
||||
|
||||
func TestAPIs(t *testing.T) {
|
||||
func TestControllers(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
|
||||
RunSpecs(t, "Controller Suite")
|
||||
|
@ -49,11 +54,21 @@ var _ = BeforeSuite(func() {
|
|||
|
||||
By("bootstrapping test environment")
|
||||
testEnv = &envtest.Environment{
|
||||
CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")},
|
||||
CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")},
|
||||
ErrorIfCRDPathMissing: true,
|
||||
|
||||
// The BinaryAssetsDirectory is only required if you want to run the tests directly
|
||||
// without call the makefile target test. If not informed it will look for the
|
||||
// default path defined in controller-runtime which is /usr/local/kubebuilder/.
|
||||
// Note that you must have the required binaries setup under the bin directory to perform
|
||||
// the tests directly. When we run make test it will be setup and used automatically.
|
||||
BinaryAssetsDirectory: filepath.Join("..", "..", "bin", "k8s",
|
||||
fmt.Sprintf("1.28.0-%s-%s", runtime.GOOS, runtime.GOARCH)),
|
||||
}
|
||||
|
||||
cfg, err := testEnv.Start()
|
||||
var err error
|
||||
// cfg is defined in this file globally.
|
||||
cfg, err = testEnv.Start()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(cfg).NotTo(BeNil())
|
||||
|
381
main.go
381
main.go
|
@ -1,381 +0,0 @@
|
|||
/*
|
||||
Copyright 2021 SPIRE Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
|
||||
// to ensure that exec-entrypoint and run can make use of them.
|
||||
"k8s.io/client-go/kubernetes"
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/healthz"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||
|
||||
"github.com/spiffe/go-spiffe/v2/spiffeid"
|
||||
|
||||
spirev1alpha1 "github.com/spiffe/spire-controller-manager/api/v1alpha1"
|
||||
"github.com/spiffe/spire-controller-manager/controllers"
|
||||
"github.com/spiffe/spire-controller-manager/pkg/spireapi"
|
||||
"github.com/spiffe/spire-controller-manager/pkg/spireentry"
|
||||
"github.com/spiffe/spire-controller-manager/pkg/spirefederationrelationship"
|
||||
"github.com/spiffe/spire-controller-manager/pkg/webhookmanager"
|
||||
//+kubebuilder:scaffold:imports
|
||||
)
|
||||
|
||||
const (
|
||||
defaultSPIREServerSocketPath = "/spire-server/api.sock"
|
||||
defaultGCInterval = 10 * time.Second
|
||||
k8sDefaultService = "kubernetes.default.svc"
|
||||
)
|
||||
|
||||
var (
|
||||
scheme = runtime.NewScheme()
|
||||
setupLog = ctrl.Log.WithName("setup")
|
||||
)
|
||||
|
||||
func init() {
|
||||
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
|
||||
|
||||
utilruntime.Must(spirev1alpha1.AddToScheme(scheme))
|
||||
//+kubebuilder:scaffold:scheme
|
||||
}
|
||||
|
||||
func main() {
|
||||
ctrlConfig, options, ignoreNamespacesRegex, err := parseConfig()
|
||||
if err != nil {
|
||||
setupLog.Error(err, "error parsing configuration")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := run(ctrlConfig, options, ignoreNamespacesRegex); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func parseConfig() (spirev1alpha1.ControllerManagerConfig, ctrl.Options, []*regexp.Regexp, error) {
|
||||
var configFileFlag string
|
||||
var spireAPISocketFlag string
|
||||
flag.StringVar(&configFileFlag, "config", "",
|
||||
"The controller will load its initial configuration from this file. "+
|
||||
"Omit this flag to use the default configuration values. "+
|
||||
"Command-line flags override configuration from this file.")
|
||||
flag.StringVar(&spireAPISocketFlag, "spire-api-socket", "", "The path to the SPIRE API socket (deprecated; use the config file)")
|
||||
|
||||
// Parse log flags
|
||||
opts := zap.Options{
|
||||
Development: true,
|
||||
}
|
||||
opts.BindFlags(flag.CommandLine)
|
||||
flag.Parse()
|
||||
|
||||
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
|
||||
|
||||
// Set default values
|
||||
ctrlConfig := spirev1alpha1.ControllerManagerConfig{
|
||||
IgnoreNamespaces: []string{"kube-system", "kube-public", "spire-system"},
|
||||
GCInterval: defaultGCInterval,
|
||||
ValidatingWebhookConfigurationName: "spire-controller-manager-webhook",
|
||||
}
|
||||
|
||||
options := ctrl.Options{Scheme: scheme}
|
||||
var ignoreNamespacesRegex []*regexp.Regexp
|
||||
|
||||
if configFileFlag != "" {
|
||||
if err := spirev1alpha1.LoadOptionsFromFile(configFileFlag, scheme, &options, &ctrlConfig); err != nil {
|
||||
return ctrlConfig, options, ignoreNamespacesRegex, fmt.Errorf("unable to load the config file: %w", err)
|
||||
}
|
||||
|
||||
for _, ignoredNamespace := range ctrlConfig.IgnoreNamespaces {
|
||||
regex, err := regexp.Compile(ignoredNamespace)
|
||||
if err != nil {
|
||||
return ctrlConfig, options, ignoreNamespacesRegex, fmt.Errorf("unable to compile ignore namespaces regex: %w", err)
|
||||
}
|
||||
|
||||
ignoreNamespacesRegex = append(ignoreNamespacesRegex, regex)
|
||||
}
|
||||
}
|
||||
// Determine the SPIRE Server socket path
|
||||
switch {
|
||||
case ctrlConfig.SPIREServerSocketPath == "" && spireAPISocketFlag == "":
|
||||
// Neither is set. Use the default.
|
||||
ctrlConfig.SPIREServerSocketPath = defaultSPIREServerSocketPath
|
||||
case ctrlConfig.SPIREServerSocketPath != "" && spireAPISocketFlag == "":
|
||||
// Configuration file value is set. Use it.
|
||||
case ctrlConfig.SPIREServerSocketPath == "" && spireAPISocketFlag != "":
|
||||
// Deprecated flag value is set. Use it but warn.
|
||||
ctrlConfig.SPIREServerSocketPath = spireAPISocketFlag
|
||||
setupLog.Error(nil, "The spire-api-socket flag is deprecated and will be removed in a future release; use the configuration file instead")
|
||||
case ctrlConfig.SPIREServerSocketPath != "" && spireAPISocketFlag != "":
|
||||
// Both are set. Warn and ignore the deprecated flag.
|
||||
setupLog.Error(nil, "Ignoring deprecated spire-api-socket flag which will be removed in a future release")
|
||||
}
|
||||
|
||||
// Attempt to auto detect cluster domain if it wasn't specified
|
||||
if ctrlConfig.ClusterDomain == "" {
|
||||
clusterDomain, err := autoDetectClusterDomain()
|
||||
if err != nil {
|
||||
setupLog.Error(err, "unable to autodetect cluster domain")
|
||||
}
|
||||
|
||||
ctrlConfig.ClusterDomain = clusterDomain
|
||||
}
|
||||
|
||||
setupLog.Info("Config loaded",
|
||||
"cluster name", ctrlConfig.ClusterName,
|
||||
"cluster domain", ctrlConfig.ClusterDomain,
|
||||
"trust domain", ctrlConfig.TrustDomain,
|
||||
"ignore namespaces", ctrlConfig.IgnoreNamespaces,
|
||||
"gc interval", ctrlConfig.GCInterval,
|
||||
"spire server socket path", ctrlConfig.SPIREServerSocketPath)
|
||||
|
||||
switch {
|
||||
case ctrlConfig.TrustDomain == "":
|
||||
setupLog.Error(nil, "trust domain is required configuration")
|
||||
return ctrlConfig, options, ignoreNamespacesRegex, errors.New("trust domain is required configuration")
|
||||
case ctrlConfig.ClusterName == "":
|
||||
return ctrlConfig, options, ignoreNamespacesRegex, errors.New("cluster name is required configuration")
|
||||
case ctrlConfig.ValidatingWebhookConfigurationName == "":
|
||||
return ctrlConfig, options, ignoreNamespacesRegex, errors.New("validating webhook configuration name is required configuration")
|
||||
case ctrlConfig.ControllerManagerConfigurationSpec.Webhook.CertDir != "":
|
||||
setupLog.Info("certDir configuration is ignored", "certDir", ctrlConfig.ControllerManagerConfigurationSpec.Webhook.CertDir)
|
||||
}
|
||||
|
||||
return ctrlConfig, options, ignoreNamespacesRegex, nil
|
||||
}
|
||||
|
||||
func run(ctrlConfig spirev1alpha1.ControllerManagerConfig, options ctrl.Options, ignoreNamespacesRegex []*regexp.Regexp) error {
|
||||
// It's unfortunate that we have to keep credentials on disk so that the
|
||||
// manager can load them:
|
||||
// TODO: upstream a change to the WebhookServer so it can use callbacks to
|
||||
// obtain the certificates so we don't have to touch disk.
|
||||
certDir, err := os.MkdirTemp("", "spire-controller-manager-")
|
||||
if err != nil {
|
||||
setupLog.Error(err, "failed to create temporary cert directory")
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err := os.RemoveAll(certDir); err != nil {
|
||||
setupLog.Error(err, "failed to remove temporary cert directory", "certDir", certDir)
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
|
||||
// webhook server credentials are stored in a single file to keep rotation
|
||||
// simple.
|
||||
const keyPairName = "keypair.pem"
|
||||
options.WebhookServer = webhook.NewServer(webhook.Options{
|
||||
CertDir: certDir,
|
||||
CertName: keyPairName,
|
||||
KeyName: keyPairName,
|
||||
TLSOpts: []func(*tls.Config){
|
||||
func(s *tls.Config) {
|
||||
s.MinVersion = tls.VersionTLS12
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
ctx := ctrl.SetupSignalHandler()
|
||||
|
||||
trustDomain, err := spiffeid.TrustDomainFromString(ctrlConfig.TrustDomain)
|
||||
if err != nil {
|
||||
setupLog.Error(err, "invalid trust domain name")
|
||||
return err
|
||||
}
|
||||
setupLog.Info("Dialing SPIRE Server socket")
|
||||
spireClient, err := spireapi.DialSocket(ctx, ctrlConfig.SPIREServerSocketPath)
|
||||
if err != nil {
|
||||
setupLog.Error(err, "unable to dial SPIRE Server socket")
|
||||
return err
|
||||
}
|
||||
defer spireClient.Close()
|
||||
|
||||
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), options)
|
||||
if err != nil {
|
||||
setupLog.Error(err, "unable to start manager")
|
||||
return err
|
||||
}
|
||||
|
||||
// We need a direct client to query and patch up the webhook. We can't use
|
||||
// the controller runtime client for this because we can't start the manager
|
||||
// without the webhook credentials being in place, and the webhook credentials
|
||||
// need the DNS name of the webhook service from the configuration.
|
||||
config, err := rest.InClusterConfig()
|
||||
if err != nil {
|
||||
setupLog.Error(err, "failed to get in cluster configuration")
|
||||
return err
|
||||
}
|
||||
// creates the clientset
|
||||
clientset, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
setupLog.Error(err, "failed to create an API client")
|
||||
return err
|
||||
}
|
||||
|
||||
webhookID, _ := spiffeid.FromPath(trustDomain, "/spire-controller-manager-webhook")
|
||||
webhookManager := webhookmanager.New(webhookmanager.Config{
|
||||
ID: webhookID,
|
||||
KeyPairPath: filepath.Join(certDir, keyPairName),
|
||||
WebhookName: ctrlConfig.ValidatingWebhookConfigurationName,
|
||||
WebhookClient: clientset.AdmissionregistrationV1().ValidatingWebhookConfigurations(),
|
||||
SVIDClient: spireClient,
|
||||
BundleClient: spireClient,
|
||||
})
|
||||
|
||||
if err := webhookManager.Init(ctx); err != nil {
|
||||
setupLog.Error(err, "failed to mint initial webhook certificate")
|
||||
return err
|
||||
}
|
||||
|
||||
entryReconciler := spireentry.Reconciler(spireentry.ReconcilerConfig{
|
||||
TrustDomain: trustDomain,
|
||||
ClusterName: ctrlConfig.ClusterName,
|
||||
ClusterDomain: ctrlConfig.ClusterDomain,
|
||||
K8sClient: mgr.GetClient(),
|
||||
EntryClient: spireClient,
|
||||
IgnoreNamespaces: ignoreNamespacesRegex,
|
||||
GCInterval: ctrlConfig.GCInterval,
|
||||
})
|
||||
|
||||
federationRelationshipReconciler := spirefederationrelationship.Reconciler(spirefederationrelationship.ReconcilerConfig{
|
||||
K8sClient: mgr.GetClient(),
|
||||
TrustDomainClient: spireClient,
|
||||
GCInterval: ctrlConfig.GCInterval,
|
||||
})
|
||||
|
||||
if err = (&controllers.ClusterSPIFFEIDReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
Triggerer: entryReconciler,
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "ClusterSPIFFEID")
|
||||
return err
|
||||
}
|
||||
if err = (&controllers.ClusterFederatedTrustDomainReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
Triggerer: federationRelationshipReconciler,
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "ClusterFederatedTrustDomain")
|
||||
return err
|
||||
}
|
||||
if err = (&controllers.ClusterStaticEntryReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
Triggerer: entryReconciler,
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "ClusterStaticEntry")
|
||||
return err
|
||||
}
|
||||
if err = (&spirev1alpha1.ClusterFederatedTrustDomain{}).SetupWebhookWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create webhook", "webhook", "ClusterFederatedTrustDomain")
|
||||
return err
|
||||
}
|
||||
if err = (&spirev1alpha1.ClusterSPIFFEID{}).SetupWebhookWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create webhook", "webhook", "ClusterSPIFFEID")
|
||||
return err
|
||||
}
|
||||
//+kubebuilder:scaffold:builder
|
||||
|
||||
if err = (&controllers.PodReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
Triggerer: entryReconciler,
|
||||
IgnoreNamespaces: ignoreNamespacesRegex,
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "Pod")
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mgr.Add(manager.RunnableFunc(entryReconciler.Run)); err != nil {
|
||||
setupLog.Error(err, "unable to manage entry reconciler")
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mgr.Add(manager.RunnableFunc(federationRelationshipReconciler.Run)); err != nil {
|
||||
setupLog.Error(err, "unable to manage federation relationship reconciler")
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mgr.Add(webhookManager); err != nil {
|
||||
setupLog.Error(err, "unable to manage federation relationship reconciler")
|
||||
return err
|
||||
}
|
||||
|
||||
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
|
||||
setupLog.Error(err, "unable to set up health check")
|
||||
return err
|
||||
}
|
||||
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
|
||||
setupLog.Error(err, "unable to set up ready check")
|
||||
return err
|
||||
}
|
||||
|
||||
setupLog.Info("starting manager")
|
||||
if err := mgr.Start(ctx); err != nil {
|
||||
setupLog.Error(err, "problem running manager")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func autoDetectClusterDomain() (string, error) {
|
||||
cname, err := net.LookupCNAME(k8sDefaultService)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to lookup CNAME: %w", err)
|
||||
}
|
||||
|
||||
clusterDomain, err := parseClusterDomainCNAME(cname)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to parse CNAME \"%s\": %w", cname, err)
|
||||
}
|
||||
|
||||
return clusterDomain, nil
|
||||
}
|
||||
|
||||
func parseClusterDomainCNAME(cname string) (string, error) {
|
||||
clusterDomain := strings.TrimPrefix(cname, k8sDefaultService+".")
|
||||
if clusterDomain == cname {
|
||||
return "", errors.New("CNAME did not have expected prefix")
|
||||
}
|
||||
|
||||
// Trim off optional trailing dot
|
||||
clusterDomain = strings.TrimSuffix(clusterDomain, ".")
|
||||
if clusterDomain == "" {
|
||||
return "", errors.New("CNAME did not have a cluster domain")
|
||||
}
|
||||
|
||||
return clusterDomain, nil
|
||||
}
|
|
@ -31,6 +31,7 @@ Next deploy the new SPIRE Controller Manager.
|
|||
```shell
|
||||
kubectl apply -f ../config/crd/bases/spire.spiffe.io_clusterspiffeids.yaml \
|
||||
-f ../config/crd/bases/spire.spiffe.io_clusterfederatedtrustdomains.yaml \
|
||||
-f ../config/crd/bases/spire.spiffe.io_clusterstaticentries.yaml \
|
||||
-f config/spire-controller-manager-webhook.yaml \
|
||||
-f config/leader_election_role.yaml \
|
||||
-f config/leader_election_role_binding.yaml \
|
||||
|
@ -46,11 +47,11 @@ Next deploy the new SPIRE Controller Manager.
|
|||
```
|
||||
|
||||
> **Note**
|
||||
> See [FAQs](#faqs) for instructions on how to translate [label](#how-do-i-do-label-based-workload-registration), [annotation](#how-do-i-do-annotation-based-workload-registration), and [service account](#how-do-i-do-service-account-based-workload-registration) based workload registration. Also see [ClusterSPIFFEID definition](https://github.com/spiffe/spire-controller-manager/blob/main/docs/clusterspiffeid-crd.md) for more information on how to create the most suitable shape for your environment.
|
||||
> See [FAQs](#faqs) for instructions on how to translate [label](#how-do-i-do-label-based-workload-registration), [annotation](#how-do-i-do-annotation-based-workload-registration), and [service account](#how-do-i-do-service-account-based-workload-registration) based workload registration. Also see [ClusterSPIFFEID definition][1] for more information on how to create the most suitable shape for your environment.
|
||||
|
||||
## Delete the Kubernetes Workload Registrar CRD (CRD mode only)
|
||||
|
||||
The CRD mode requires an additonal step of removing the SpiffeId CRD. SPIRE Controller Manager uses a different CRD, so this one needs to be removed and resources cleaned up.
|
||||
The CRD mode requires an additional step of removing the SpiffeId CRD. SPIRE Controller Manager uses a different CRD, so this one needs to be removed and resources cleaned up.
|
||||
|
||||
1. Manually remove the finalizers with the below script. SPIRE Controller Manager will automatically clean up entries, so the finalizers can safely be removed.
|
||||
|
||||
|
@ -145,7 +146,7 @@ spec:
|
|||
The `matchExpressions` statement will select only Pods with the `spiffe.io/spiffe-id` label. For Pods with this label, the `spiffeIDTemplate` will extract the value of this label and use it to form the SPIFFE ID.
|
||||
|
||||
> **Note**
|
||||
> Allowing the value of labels to directly populate a SPIFFE ID gives the power to create arbitrary SPIFFE IDs to anyone that can deploy a Pod in your cluster. It's better to define a SPIFFE ID using a template that doesn't depend on a label. See [ClusterSPIFFEID defintion](https://github.com/spiffe/spire-controller-manager/blob/main/docs/clusterspiffeid-crd.md) for more information.
|
||||
> Allowing the value of labels to directly populate a SPIFFE ID gives the power to create arbitrary SPIFFE IDs to anyone that can deploy a Pod in your cluster. It's better to define a SPIFFE ID using a template that doesn't depend on a label. See [ClusterSPIFFEID defintion][1] for more information.
|
||||
|
||||
### How do I do annotation based workload registration?
|
||||
|
||||
|
@ -165,7 +166,7 @@ spec:
|
|||
```
|
||||
|
||||
> **Note**
|
||||
> This will create an entry for every Pod in the system. For use cases where every Pod needs a certificate this configuration will work well. If you prefer to limit what Pods get a certificate, restrict it with a label like in the main example in `config/clusterspiffeid.yaml`. Also see [ClusterSPIFFEID defintion](https://github.com/spiffe/spire-controller-manager/blob/main/docs/clusterspiffeid-crd.md) for more information.
|
||||
> This will create an entry for every Pod in the system. For use cases where every Pod needs a certificate this configuration will work well. If you prefer to limit what Pods get a certificate, restrict it with a label like in the main example in `config/clusterspiffeid.yaml`. Also see [ClusterSPIFFEID defintion][1] for more information.
|
||||
|
||||
### How do I federate trust domains?
|
||||
|
||||
|
@ -181,7 +182,7 @@ metadata:
|
|||
...
|
||||
```
|
||||
|
||||
The equivalent with SPIRE Controller Manager is accomplished with the `federatesWith` field of the [ClusterSPIFFEID CRD](https://github.com/spiffe/spire-controller-manager/blob/main/docs/clusterspiffeid-crd.md).
|
||||
The equivalent with SPIRE Controller Manager is accomplished with the `federatesWith` field of the [ClusterSPIFFEID CRD][1].
|
||||
|
||||
```yaml
|
||||
apiVersion: spire.spiffe.io/v1alpha1
|
||||
|
@ -199,7 +200,7 @@ spec:
|
|||
|
||||
### How do I add DNS names to my certificates?
|
||||
|
||||
You can add multiple DNS names with the `dnsNameTemplates` field of the [ClusterSPIFFEID CRD](https://github.com/spiffe/spire-controller-manager/blob/main/docs/clusterspiffeid-crd.md).
|
||||
You can add multiple DNS names with the `dnsNameTemplates` field of the [ClusterSPIFFEID CRD][1].
|
||||
|
||||
```yaml
|
||||
apiVersion: spire.spiffe.io/v1alpha1
|
||||
|
@ -217,31 +218,21 @@ spec:
|
|||
|
||||
### Does SPIRE Controller Manager automatically populate DNS Names of Services a Pod is attached to?
|
||||
|
||||
SPIRE Controller Manager doesn't monitor Endpoints like Kubernetes Workload Registrar did, so it won't do this automatically. A workaround is to use the `app` label to populate DNS Names using `dnsNameTemplates` field of the [ClusterSPIFFEID CRD](https://github.com/spiffe/spire-controller-manager/blob/main/docs/clusterspiffeid-crd.md), assuming you are using `app` as your selector and it matches the name of the `Service`.
|
||||
Yes, this is enabled with the sample configuration in this migration guide.
|
||||
|
||||
```yaml
|
||||
apiVersion: spire.spiffe.io/v1alpha1
|
||||
kind: ClusterSPIFFEID
|
||||
metadata:
|
||||
name: federation
|
||||
spec:
|
||||
spiffeIDTemplate: "spiffe://{{ .TrustDomain }}/ns/{{ .PodMeta.Namespace }}/sa/{{ .PodSpec.ServiceAccountName }}"
|
||||
podSelector:
|
||||
matchLabels:
|
||||
spiffe.io/spiffe-id: "true"
|
||||
dnsNameTemplates: ["{{ index .PodMeta.Labels \"app\" }}.{{ .PodMeta.Namespace }}.svc.cluster.local"]
|
||||
For each [ClusterSPIFFEID][1] you want to auto populate DNS names for, set the `autoPopulateDNSNames` field there. See [example](config/clusterspiffeid.yaml).
|
||||
|
||||
```
|
||||
|
||||
If you require these DNS Names to be automatically populated, please update [#48](https://github.com/spiffe/spire-controller-manager/issues/48) with your use case.
|
||||
> **Note**
|
||||
> Spire Controller Manager 0.4.0 or later is required to auto populate DNS names.
|
||||
|
||||
### Can SPIRE Controller Manager be deployed in a different Pod from SPIRE Server?
|
||||
|
||||
This is not supported with SPIRE Controller Manager, they must be in the same Pod. If you require them to be in seperate Pods, please open a [new issue](https://github.com/spiffe/spire-controller-manager/issues/new) with your use case.
|
||||
This is not supported with SPIRE Controller Manager, they must be in the same Pod. If you require them to be in separate Pods, please open a [new issue](https://github.com/spiffe/spire-controller-manager/issues/new) with your use case.
|
||||
|
||||
### Can I manually create entries like I could with the CRD Kubernetes Workload Registrar?
|
||||
|
||||
This is not currently supported, SPIRE Controller Manager will automatically garbage collect any manually created entries. If you need suppport for manually created entries, please update [#76](https://github.com/spiffe/spire-controller-manager/issues/76) with your use case.
|
||||
Yes, but it requires the use of a separate CRD ([ClusterStaticEntry][2]).
|
||||
|
||||
### How do i see SPIRE Controller Manager logs?
|
||||
|
||||
|
@ -254,7 +245,7 @@ $ kubectl logs spire-server-0 -n spire -c spire-controller-manager
|
|||
2022-12-13T00:41:21.844Z INFO webhook-manager Webhook configuration patched with CABundle
|
||||
```
|
||||
|
||||
### I'm using CRD mode Kubernetes Workload Registrar and it gets stuck deleting the SpiffeId CRD. What do I do?
|
||||
### I'm using CRD mode Kubernetes Workload Registrar, and it gets stuck deleting the SpiffeId CRD. What do I do?
|
||||
|
||||
This can happen if the Kubernetes Workload Registrar is deleted before all the SpiffeId custom resources are removed. To get around this, manually remove the finalizers with the below script and try deleting the CRD again.
|
||||
|
||||
|
@ -270,8 +261,11 @@ done
|
|||
|
||||
### Why can't Kubernetes Workload Registrar entries be reused with SPIRE Controller Manager?
|
||||
|
||||
SPIRE Controller Manager uses a different scheme for parenting SPIFFE IDs. Though it is technically possible to modify all the entries, its a lot easier to just allow SPIRE Controller Manager to automatically replace the entries.
|
||||
SPIRE Controller Manager uses a different scheme for parenting SPIFFE IDs. Though it is technically possible to modify all the entries, it's a lot easier to just allow SPIRE Controller Manager to automatically replace the entries.
|
||||
|
||||
### What happens if a Pod is deployed while I'm in the middle of this cutover?
|
||||
### What happens if a Pod is deployed while I'm in the middle of this cut-over?
|
||||
|
||||
SPIRE Controller Manager will reconcile the state of the system when it starts up. Any new Pods deployed after Kubernetes Workload Registrar is deleted and before SPIRE Controller Manager is up will have entries created when SPIRE Controller Manager is up.
|
||||
|
||||
[1]: docs/clusterspiffeid-crd.md
|
||||
[2]: docs/clusterstaticentry-crd.md
|
||||
|
|
|
@ -4,6 +4,7 @@ metadata:
|
|||
name: example
|
||||
spec:
|
||||
spiffeIDTemplate: "spiffe://{{ .TrustDomain }}/ns/{{ .PodMeta.Namespace }}/sa/{{ .PodSpec.ServiceAccountName }}"
|
||||
autoPopulateDNSNames: true
|
||||
podSelector:
|
||||
matchLabels:
|
||||
spiffe.io/spiffe-id: "true"
|
||||
|
|
|
@ -15,6 +15,9 @@ rules:
|
|||
- apiGroups: [""]
|
||||
resources: ["pods"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: [""]
|
||||
resources: ["endpoints"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: ["spire.spiffe.io"]
|
||||
resources: ["clusterfederatedtrustdomains"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||
|
@ -33,3 +36,9 @@ rules:
|
|||
- apiGroups: ["spire.spiffe.io"]
|
||||
resources: ["clusterspiffeids/status"]
|
||||
verbs: ["get", "patch", "update"]
|
||||
- apiGroups: ["spire.spiffe.io"]
|
||||
resources: ["clusterstaticentries"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||
- apiGroups: ["spire.spiffe.io"]
|
||||
resources: ["clusterstaticentries/status"]
|
||||
verbs: ["get"]
|
||||
|
|
|
@ -176,7 +176,7 @@ spec:
|
|||
shareProcessNamespace: true
|
||||
containers:
|
||||
- name: spire-server
|
||||
image: ghcr.io/spiffe/spire-server:1.5.4
|
||||
image: ghcr.io/spiffe/spire-server:1.11.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
args: ["-config", "/run/spire/server/config/server.conf"]
|
||||
ports:
|
||||
|
@ -190,7 +190,7 @@ spec:
|
|||
- name: spire-server-socket
|
||||
mountPath: /tmp/spire-server/private
|
||||
- name: spire-controller-manager
|
||||
image: ghcr.io/spiffe/spire-controller-manager:nightly
|
||||
image: ghcr.io/spiffe/spire-controller-manager:0.6.2
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 9443
|
||||
|
|
|
@ -168,6 +168,6 @@ type failList struct {
|
|||
client.Client
|
||||
}
|
||||
|
||||
func (c failList) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {
|
||||
func (c failList) List(context.Context, client.ObjectList, ...client.ListOption) error {
|
||||
return errList
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package metrics
|
||||
|
||||
import "github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
const (
|
||||
StaticEntryFailures = "cluster_static_entry_failures"
|
||||
)
|
||||
|
||||
var (
|
||||
PromCounters = map[string]prometheus.Counter{
|
||||
StaticEntryFailures: prometheus.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Name: StaticEntryFailures,
|
||||
Help: "Number of cluster static entry render failures",
|
||||
},
|
||||
),
|
||||
}
|
||||
)
|
|
@ -25,6 +25,8 @@ import (
|
|||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
)
|
||||
|
||||
const EndpointUID string = "subsets.addresses.targetRef.uid"
|
||||
|
||||
type Triggerer interface {
|
||||
Trigger()
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ type bundleServer struct {
|
|||
bundle *apitypes.Bundle
|
||||
}
|
||||
|
||||
func (s *bundleServer) GetBundle(ctx context.Context, req *bundlev1.GetBundleRequest) (*apitypes.Bundle, error) {
|
||||
func (s *bundleServer) GetBundle(context.Context, *bundlev1.GetBundleRequest) (*apitypes.Bundle, error) {
|
||||
s.mtx.RLock()
|
||||
bundle := s.bundle
|
||||
s.mtx.RUnlock()
|
||||
|
|
|
@ -17,11 +17,9 @@ limitations under the License.
|
|||
package spireapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
|
@ -35,7 +33,7 @@ type Client interface {
|
|||
io.Closer
|
||||
}
|
||||
|
||||
func DialSocket(ctx context.Context, path string) (Client, error) {
|
||||
func DialSocket(path string) (Client, error) {
|
||||
var target string
|
||||
if filepath.IsAbs(path) {
|
||||
target = "unix://" + path
|
||||
|
@ -43,9 +41,7 @@ func DialSocket(ctx context.Context, path string) (Client, error) {
|
|||
target = "unix:" + path
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
grpcClient, err := grpc.DialContext(ctx, target, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock())
|
||||
grpcClient, err := grpc.NewClient(target, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to dial API socket: %w", err)
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ func startServer(t *testing.T, registerFn func(s *grpc.Server)) grpc.ClientConnI
|
|||
go func() { _ = s.Serve(listener) }()
|
||||
t.Cleanup(s.GracefulStop)
|
||||
|
||||
conn, err := grpc.DialContext(context.Background(), listener.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.FailOnNonTempDialError(true), grpc.WithReturnConnectionError())
|
||||
conn, err := grpc.NewClient(listener.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
_ = conn.Close()
|
||||
|
|
|
@ -35,6 +35,7 @@ const (
|
|||
FederatesWithField Field = "federatesWith"
|
||||
HintField Field = "hint"
|
||||
JWTSVIDTTLField Field = "jwtSVIDTTL"
|
||||
StoreSVIDField Field = "storeSVID"
|
||||
X509SVIDTTL Field = "x509SVIDTTL"
|
||||
)
|
||||
|
||||
|
@ -96,6 +97,7 @@ func (c entryClient) GetUnsupportedFields(ctx context.Context, td string) (map[F
|
|||
},
|
||||
X509SvidTtl: 60,
|
||||
JwtSvidTtl: 60,
|
||||
StoreSvid: true,
|
||||
Hint: "hint",
|
||||
},
|
||||
},
|
||||
|
@ -132,6 +134,10 @@ func (c entryClient) GetUnsupportedFields(ctx context.Context, td string) (map[F
|
|||
unsupportedFields[HintField] = struct{}{}
|
||||
}
|
||||
|
||||
if !result.Entry.StoreSvid {
|
||||
unsupportedFields[StoreSVIDField] = struct{}{}
|
||||
}
|
||||
|
||||
return unsupportedFields, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -177,6 +177,7 @@ func TestGetUnsupportedFields(t *testing.T) {
|
|||
expectFields: map[Field]struct{}{
|
||||
HintField: {},
|
||||
JWTSVIDTTLField: {},
|
||||
StoreSVIDField: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -191,6 +192,7 @@ func TestGetUnsupportedFields(t *testing.T) {
|
|||
expectFields: map[Field]struct{}{
|
||||
HintField: {},
|
||||
JWTSVIDTTLField: {},
|
||||
StoreSVIDField: {},
|
||||
},
|
||||
},
|
||||
} {
|
||||
|
@ -375,7 +377,7 @@ type entryServer struct {
|
|||
batchDeleteEntriesErr error
|
||||
}
|
||||
|
||||
func (s *entryServer) ListEntries(ctx context.Context, req *entryv1.ListEntriesRequest) (*entryv1.ListEntriesResponse, error) {
|
||||
func (s *entryServer) ListEntries(_ context.Context, req *entryv1.ListEntriesRequest) (*entryv1.ListEntriesResponse, error) {
|
||||
resp := new(entryv1.ListEntriesResponse)
|
||||
|
||||
s.mtx.RLock()
|
||||
|
@ -392,7 +394,7 @@ func (s *entryServer) ListEntries(ctx context.Context, req *entryv1.ListEntriesR
|
|||
return resp, s.listEntriesErr
|
||||
}
|
||||
|
||||
func (s *entryServer) BatchCreateEntry(ctx context.Context, req *entryv1.BatchCreateEntryRequest) (*entryv1.BatchCreateEntryResponse, error) {
|
||||
func (s *entryServer) BatchCreateEntry(_ context.Context, req *entryv1.BatchCreateEntryRequest) (*entryv1.BatchCreateEntryResponse, error) {
|
||||
resp := new(entryv1.BatchCreateEntryResponse)
|
||||
|
||||
for _, entry := range req.Entries {
|
||||
|
@ -400,6 +402,7 @@ func (s *entryServer) BatchCreateEntry(ctx context.Context, req *entryv1.BatchCr
|
|||
if s.clearUnsupportedFields {
|
||||
entry.JwtSvidTtl = 0
|
||||
entry.Hint = ""
|
||||
entry.StoreSvid = false
|
||||
}
|
||||
|
||||
st := status.Convert(s.createEntry(entry))
|
||||
|
@ -418,7 +421,7 @@ func (s *entryServer) BatchCreateEntry(ctx context.Context, req *entryv1.BatchCr
|
|||
return resp, s.batchCreateEntriesErr
|
||||
}
|
||||
|
||||
func (s *entryServer) BatchUpdateEntry(ctx context.Context, req *entryv1.BatchUpdateEntryRequest) (*entryv1.BatchUpdateEntryResponse, error) {
|
||||
func (s *entryServer) BatchUpdateEntry(_ context.Context, req *entryv1.BatchUpdateEntryRequest) (*entryv1.BatchUpdateEntryResponse, error) {
|
||||
resp := new(entryv1.BatchUpdateEntryResponse)
|
||||
|
||||
for _, entry := range req.Entries {
|
||||
|
@ -437,7 +440,7 @@ func (s *entryServer) BatchUpdateEntry(ctx context.Context, req *entryv1.BatchUp
|
|||
return resp, s.batchUpdateEntriesErr
|
||||
}
|
||||
|
||||
func (s *entryServer) BatchDeleteEntry(ctx context.Context, req *entryv1.BatchDeleteEntryRequest) (*entryv1.BatchDeleteEntryResponse, error) {
|
||||
func (s *entryServer) BatchDeleteEntry(_ context.Context, req *entryv1.BatchDeleteEntryRequest) (*entryv1.BatchDeleteEntryResponse, error) {
|
||||
resp := new(entryv1.BatchDeleteEntryResponse)
|
||||
|
||||
for _, id := range req.Ids {
|
||||
|
|
|
@ -142,7 +142,7 @@ type svidServer struct {
|
|||
mutateResponse func(*svidv1.MintX509SVIDResponse) error
|
||||
}
|
||||
|
||||
func (s *svidServer) MintX509SVID(ctx context.Context, req *svidv1.MintX509SVIDRequest) (*svidv1.MintX509SVIDResponse, error) {
|
||||
func (s *svidServer) MintX509SVID(_ context.Context, req *svidv1.MintX509SVIDRequest) (*svidv1.MintX509SVIDResponse, error) {
|
||||
csr, err := x509.ParseCertificateRequest(req.Csr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -310,7 +310,7 @@ type trustDomainServer struct {
|
|||
batchDeleteFederationRelationshipsErr error
|
||||
}
|
||||
|
||||
func (s *trustDomainServer) ListFederationRelationships(ctx context.Context, req *trustdomainv1.ListFederationRelationshipsRequest) (*trustdomainv1.ListFederationRelationshipsResponse, error) {
|
||||
func (s *trustDomainServer) ListFederationRelationships(_ context.Context, req *trustdomainv1.ListFederationRelationshipsRequest) (*trustdomainv1.ListFederationRelationshipsResponse, error) {
|
||||
resp := new(trustdomainv1.ListFederationRelationshipsResponse)
|
||||
|
||||
s.mtx.RLock()
|
||||
|
@ -327,7 +327,7 @@ func (s *trustDomainServer) ListFederationRelationships(ctx context.Context, req
|
|||
return resp, s.listFederationRelationshipsErr
|
||||
}
|
||||
|
||||
func (s *trustDomainServer) BatchCreateFederationRelationship(ctx context.Context, req *trustdomainv1.BatchCreateFederationRelationshipRequest) (*trustdomainv1.BatchCreateFederationRelationshipResponse, error) {
|
||||
func (s *trustDomainServer) BatchCreateFederationRelationship(_ context.Context, req *trustdomainv1.BatchCreateFederationRelationshipRequest) (*trustdomainv1.BatchCreateFederationRelationshipResponse, error) {
|
||||
resp := new(trustdomainv1.BatchCreateFederationRelationshipResponse)
|
||||
|
||||
for _, fr := range req.FederationRelationships {
|
||||
|
@ -347,7 +347,7 @@ func (s *trustDomainServer) BatchCreateFederationRelationship(ctx context.Contex
|
|||
return resp, s.batchCreateFederationRelationshipsErr
|
||||
}
|
||||
|
||||
func (s *trustDomainServer) BatchUpdateFederationRelationship(ctx context.Context, req *trustdomainv1.BatchUpdateFederationRelationshipRequest) (*trustdomainv1.BatchUpdateFederationRelationshipResponse, error) {
|
||||
func (s *trustDomainServer) BatchUpdateFederationRelationship(_ context.Context, req *trustdomainv1.BatchUpdateFederationRelationshipRequest) (*trustdomainv1.BatchUpdateFederationRelationshipResponse, error) {
|
||||
resp := new(trustdomainv1.BatchUpdateFederationRelationshipResponse)
|
||||
|
||||
for _, fr := range req.FederationRelationships {
|
||||
|
@ -366,7 +366,7 @@ func (s *trustDomainServer) BatchUpdateFederationRelationship(ctx context.Contex
|
|||
return resp, s.batchUpdateFederationRelationshipsErr
|
||||
}
|
||||
|
||||
func (s *trustDomainServer) BatchDeleteFederationRelationship(ctx context.Context, req *trustdomainv1.BatchDeleteFederationRelationshipRequest) (*trustdomainv1.BatchDeleteFederationRelationshipResponse, error) {
|
||||
func (s *trustDomainServer) BatchDeleteFederationRelationship(_ context.Context, req *trustdomainv1.BatchDeleteFederationRelationshipRequest) (*trustdomainv1.BatchDeleteFederationRelationshipResponse, error) {
|
||||
resp := new(trustdomainv1.BatchDeleteFederationRelationshipResponse)
|
||||
|
||||
for _, td := range req.TrustDomains {
|
||||
|
|
|
@ -44,6 +44,7 @@ type Entry struct {
|
|||
Downstream bool
|
||||
DNSNames []string
|
||||
Hint string
|
||||
StoreSVID bool
|
||||
}
|
||||
|
||||
type Selector struct {
|
||||
|
@ -154,6 +155,7 @@ func entryToAPI(in Entry) *apitypes.Entry {
|
|||
DnsNames: in.DNSNames,
|
||||
Downstream: in.Downstream,
|
||||
Hint: in.Hint,
|
||||
StoreSvid: in.StoreSVID,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,6 +206,7 @@ func entryFromAPI(in *apitypes.Entry) (Entry, error) {
|
|||
DNSNames: in.DnsNames,
|
||||
Downstream: in.Downstream,
|
||||
Hint: in.Hint,
|
||||
StoreSVID: in.StoreSvid,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ var (
|
|||
Admin: true,
|
||||
Downstream: true,
|
||||
DNSNames: []string{"dnsname"},
|
||||
StoreSVID: true,
|
||||
}
|
||||
|
||||
apiEntry = &apitypes.Entry{
|
||||
|
@ -50,6 +51,7 @@ var (
|
|||
Admin: true,
|
||||
Downstream: true,
|
||||
DnsNames: []string{"dnsname"},
|
||||
StoreSvid: true,
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -85,7 +87,7 @@ func TestFederationRelationshipEqual(t *testing.T) {
|
|||
assert.False(t, base.Equal(compareTo))
|
||||
}
|
||||
|
||||
assertEqual(t, func(compareTo *FederationRelationship) {})
|
||||
assertEqual(t, func(_ *FederationRelationship) {})
|
||||
assertNotEqual(t, func(compareTo *FederationRelationship) {
|
||||
compareTo.TrustDomain = tdB
|
||||
})
|
||||
|
@ -161,7 +163,7 @@ func TestEntryFromAPI(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
desc: "nil entry",
|
||||
makeEntry: func(base *apitypes.Entry) *apitypes.Entry {
|
||||
makeEntry: func(_ *apitypes.Entry) *apitypes.Entry {
|
||||
return nil
|
||||
},
|
||||
expectErr: "entry is nil",
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
|
@ -30,6 +31,8 @@ import (
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
var defaultParentIDTemplate = template.Must(template.New("defaultParentIDTemplate").Parse("spiffe://{{ .TrustDomain }}/spire/agent/k8s_psat/{{ .ClusterName }}/{{ .NodeMeta.UID }}"))
|
||||
|
||||
func renderStaticEntry(spec *spirev1alpha1.ClusterStaticEntrySpec) (*spireapi.Entry, error) {
|
||||
spiffeID, err := spiffeid.FromString(spec.SPIFFEID)
|
||||
if err != nil {
|
||||
|
@ -62,49 +65,48 @@ func renderStaticEntry(spec *spirev1alpha1.ClusterStaticEntrySpec) (*spireapi.En
|
|||
Admin: spec.Admin,
|
||||
Downstream: spec.Downstream,
|
||||
Hint: spec.Hint,
|
||||
StoreSVID: spec.StoreSVID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func renderPodEntry(spec *spirev1alpha1.ParsedClusterSPIFFEIDSpec, node *corev1.Node, pod *corev1.Pod, trustDomain spiffeid.TrustDomain, clusterName, clusterDomain string) (*spireapi.Entry, error) {
|
||||
func renderPodEntry(spec *spirev1alpha1.ParsedClusterSPIFFEIDSpec, node *corev1.Node, pod *corev1.Pod, endpointsList *corev1.EndpointsList, trustDomain spiffeid.TrustDomain, clusterName, clusterDomain string, parentIDTemplate *template.Template) (*spireapi.Entry, error) {
|
||||
// We uniquely target the Pod running on the Node. The former is done
|
||||
// via the k8s:pod-uid selector, the latter via the parent ID.
|
||||
selectors := []spireapi.Selector{
|
||||
{Type: "k8s", Value: fmt.Sprintf("pod-uid:%s", pod.UID)},
|
||||
}
|
||||
parentID, err := spiffeid.FromPathf(trustDomain, "/spire/agent/k8s_psat/%s/%s", clusterName, node.UID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to render parent ID: %w", err)
|
||||
}
|
||||
|
||||
data := &templateData{
|
||||
TrustDomain: trustDomain.Name(),
|
||||
ClusterName: clusterName,
|
||||
ClusterDomain: clusterDomain,
|
||||
PodMeta: &pod.ObjectMeta,
|
||||
PodSpec: &pod.Spec,
|
||||
NodeMeta: &node.ObjectMeta,
|
||||
NodeSpec: &node.Spec,
|
||||
}
|
||||
|
||||
if parentIDTemplate == nil {
|
||||
parentIDTemplate = defaultParentIDTemplate
|
||||
}
|
||||
|
||||
parentID, err := renderSPIFFEID(parentIDTemplate, data, trustDomain)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to render parent ID: %w", err)
|
||||
}
|
||||
|
||||
data.PodMeta = &pod.ObjectMeta
|
||||
data.PodSpec = &pod.Spec
|
||||
|
||||
spiffeID, err := renderSPIFFEID(spec.SPIFFEIDTemplate, data, trustDomain)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to render SPIFFE ID: %w", err)
|
||||
}
|
||||
|
||||
var dnsNames []string
|
||||
dnsNamesSet := make(map[string]struct{})
|
||||
for _, dnsNameTemplate := range spec.DNSNameTemplates {
|
||||
dnsName, err := renderDNSName(dnsNameTemplate, data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to render DNS name: %w", err)
|
||||
}
|
||||
|
||||
// Only add the DNS name if it doesn't already exist
|
||||
if _, exists := dnsNamesSet[dnsName]; !exists {
|
||||
dnsNamesSet[dnsName] = struct{}{}
|
||||
dnsNames = append(dnsNames, dnsName)
|
||||
}
|
||||
dnsNames, err := renderDNSNames(dnsNamesSet, spec.DNSNameTemplates, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dnsNames = appendIfNotExists(dnsNames, dnsNamesSet, dnsNamesFromEndpoints(endpointsList, clusterDomain)...)
|
||||
|
||||
for _, workloadSelectorTemplate := range spec.WorkloadSelectorTemplates {
|
||||
selector, err := renderSelector(workloadSelectorTemplate, data)
|
||||
|
@ -124,6 +126,7 @@ func renderPodEntry(spec *spirev1alpha1.ParsedClusterSPIFFEIDSpec, node *corev1.
|
|||
DNSNames: dnsNames,
|
||||
Admin: spec.Admin,
|
||||
Downstream: spec.Downstream,
|
||||
Hint: spec.Hint,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -152,6 +155,19 @@ func renderSPIFFEID(tmpl *template.Template, data *templateData, expectTD spiffe
|
|||
return id, nil
|
||||
}
|
||||
|
||||
func renderDNSNames(dnsNamesSet map[string]struct{}, dnsNameTemplates []*template.Template, data *templateData) (dnsNames []string, err error) {
|
||||
for _, dnsNameTemplate := range dnsNameTemplates {
|
||||
dnsName, err := renderDNSName(dnsNameTemplate, data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to render DNS name: %w", err)
|
||||
}
|
||||
|
||||
dnsNames = appendIfNotExists(dnsNames, dnsNamesSet, dnsName)
|
||||
}
|
||||
|
||||
return dnsNames, nil
|
||||
}
|
||||
|
||||
func renderDNSName(tmpl *template.Template, data *templateData) (string, error) {
|
||||
rendered, err := renderTemplate(tmpl, data)
|
||||
if err != nil {
|
||||
|
@ -160,6 +176,25 @@ func renderDNSName(tmpl *template.Template, data *templateData) (string, error)
|
|||
return rendered, nil
|
||||
}
|
||||
|
||||
func dnsNamesFromEndpoints(endpointsList *corev1.EndpointsList, clusterDomain string) []string {
|
||||
var dnsNames []string
|
||||
for _, endpoint := range endpointsList.Items {
|
||||
dnsNames = append(dnsNames,
|
||||
endpoint.Name,
|
||||
endpoint.Name+"."+endpoint.Namespace,
|
||||
endpoint.Name+"."+endpoint.Namespace+".svc",
|
||||
)
|
||||
if clusterDomain != "" {
|
||||
dnsNames = append(dnsNames, endpoint.Name+"."+endpoint.Namespace+".svc."+clusterDomain)
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the list to provide consistent results
|
||||
sort.Strings(dnsNames)
|
||||
|
||||
return dnsNames
|
||||
}
|
||||
|
||||
func renderSelector(tmpl *template.Template, data *templateData) (spireapi.Selector, error) {
|
||||
rendered, err := renderTemplate(tmpl, data)
|
||||
if err != nil {
|
||||
|
@ -207,3 +242,14 @@ func parseSelector(selector string) (spireapi.Selector, error) {
|
|||
Value: parts[1],
|
||||
}, nil
|
||||
}
|
||||
|
||||
func appendIfNotExists(slice []string, sliceSet map[string]struct{}, items ...string) []string {
|
||||
for _, item := range items {
|
||||
if _, exists := sliceSet[item]; !exists {
|
||||
sliceSet[item] = struct{}{}
|
||||
slice = append(slice, item)
|
||||
}
|
||||
}
|
||||
|
||||
return slice
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package spireentry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/spiffe/go-spiffe/v2/spiffeid"
|
||||
|
@ -22,7 +24,7 @@ func TestRenderPodEntry(t *testing.T) {
|
|||
SPIFFEIDTemplate: "spiffe://{{ .TrustDomain }}/ns/{{ .PodMeta.Namespace }}/sa/{{ .PodSpec.ServiceAccountName }}",
|
||||
DNSNameTemplates: []string{
|
||||
"{{ .PodSpec.ServiceAccountName }}.{{ .PodMeta.Namespace }}.svc.{{ .ClusterDomain }}",
|
||||
"{{ .PodMeta.Name }}.{{ .PodMeta.Namespace }}.svc.{{ .ClusterDomain }}",
|
||||
"{{ .PodMeta.Name }}.{{ .PodMeta.Namespace }}.svc.{{ .ClusterDomain }}", // Duplicate
|
||||
"{{ .PodMeta.Name }}.{{ .TrustDomain }}.svc",
|
||||
},
|
||||
}
|
||||
|
@ -41,13 +43,29 @@ func TestRenderPodEntry(t *testing.T) {
|
|||
ServiceAccountName: "test",
|
||||
},
|
||||
}
|
||||
endpointsList := &corev1.EndpointsList{
|
||||
Items: []corev1.Endpoints{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "endpoint",
|
||||
Namespace: "namespace",
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "other-endpoint",
|
||||
Namespace: "namespace",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
parsedSpec, err := spirev1alpha1.ParseClusterSPIFFEIDSpec(spec)
|
||||
require.NoError(t, err)
|
||||
td, err := spiffeid.TrustDomainFromString(trustDomain)
|
||||
require.NoError(t, err)
|
||||
|
||||
entry, err := renderPodEntry(parsedSpec, node, pod, td, clusterName, clusterDomain)
|
||||
entry, err := renderPodEntry(parsedSpec, node, pod, endpointsList, td, clusterName, clusterDomain, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// SPIFFE ID rendered correctly
|
||||
|
@ -60,10 +78,28 @@ func TestRenderPodEntry(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.Equal(t, entry.ParentID.String(), parentID.String())
|
||||
|
||||
// DNS names rendered correctly and are unique
|
||||
require.Len(t, entry.DNSNames, len(spec.DNSNameTemplates)-1)
|
||||
require.Contains(t, entry.DNSNames, pod.Name+"."+pod.Namespace+".svc."+clusterDomain)
|
||||
require.Contains(t, entry.DNSNames, pod.Name+"."+trustDomain+".svc")
|
||||
// DNS names are unique
|
||||
dnsNamesSet := make(map[string]struct{})
|
||||
for _, dnsName := range entry.DNSNames {
|
||||
_, exists := dnsNamesSet[dnsName]
|
||||
require.False(t, exists)
|
||||
dnsNamesSet[dnsName] = struct{}{}
|
||||
}
|
||||
|
||||
// DNS names list is as long as expected
|
||||
require.Equal(t, len(spec.DNSNameTemplates)-1+len(endpointsList.Items)*4, len(entry.DNSNames))
|
||||
|
||||
// DNS names templates rendered correctly and are in order
|
||||
require.Equal(t, entry.DNSNames[0], pod.Spec.ServiceAccountName+"."+pod.Namespace+".svc."+clusterDomain)
|
||||
require.Equal(t, entry.DNSNames[1], pod.Name+"."+trustDomain+".svc")
|
||||
|
||||
// Endpoint DNS Names auto populated
|
||||
for _, endpoint := range endpointsList.Items {
|
||||
require.Contains(t, entry.DNSNames, endpoint.Name)
|
||||
require.Contains(t, entry.DNSNames, endpoint.Name+"."+endpoint.Namespace)
|
||||
require.Contains(t, entry.DNSNames, endpoint.Name+"."+endpoint.Namespace+".svc")
|
||||
require.Contains(t, entry.DNSNames, endpoint.Name+"."+endpoint.Namespace+".svc."+clusterDomain)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJWTTTLInRenderPodEntry(t *testing.T) {
|
||||
|
@ -93,8 +129,44 @@ func TestJWTTTLInRenderPodEntry(t *testing.T) {
|
|||
td, err := spiffeid.TrustDomainFromString(trustDomain)
|
||||
require.NoError(t, err)
|
||||
|
||||
entry, err := renderPodEntry(parsedSpec, node, pod, td, clusterName, clusterDomain)
|
||||
entry, err := renderPodEntry(parsedSpec, node, pod, &corev1.EndpointsList{}, td, clusterName, clusterDomain, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, entry.JWTSVIDTTL.Nanoseconds(), spec.JWTTTL.Nanoseconds())
|
||||
}
|
||||
|
||||
func TestParentIDTemplateRenderPodEntry(t *testing.T) {
|
||||
spec := &spirev1alpha1.ClusterSPIFFEIDSpec{
|
||||
SPIFFEIDTemplate: "spiffe://{{ .TrustDomain }}/ns/{{ .PodMeta.Namespace }}/sa/{{ .PodSpec.ServiceAccountName }}",
|
||||
}
|
||||
|
||||
node := &corev1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: "uid",
|
||||
Name: "test.example.org",
|
||||
},
|
||||
Spec: corev1.NodeSpec{},
|
||||
}
|
||||
pod := &corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
Namespace: "namespace",
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
ServiceAccountName: "test",
|
||||
},
|
||||
}
|
||||
|
||||
defaultParentIDTemplate, err := template.New("testParentIDTemplate").Parse("spiffe://{{ .TrustDomain }}/spire/agent/x509pop/{{ .NodeMeta.Name }}")
|
||||
require.NoError(t, err)
|
||||
|
||||
parsedSpec, err := spirev1alpha1.ParseClusterSPIFFEIDSpec(spec)
|
||||
require.NoError(t, err)
|
||||
td, err := spiffeid.TrustDomainFromString(trustDomain)
|
||||
require.NoError(t, err)
|
||||
|
||||
entry, err := renderPodEntry(parsedSpec, node, pod, &corev1.EndpointsList{}, td, clusterName, clusterDomain, defaultParentIDTemplate)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, entry.ParentID.String(), fmt.Sprintf("spiffe://%s/spire/agent/x509pop/test.example.org", td))
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ const (
|
|||
adminKey = "admin"
|
||||
downstreamKey = "downstream"
|
||||
hintKey = "hint"
|
||||
storeSVIDKey = "storeSVID"
|
||||
)
|
||||
|
||||
func objectName(o metav1.Object) string {
|
||||
|
@ -64,6 +65,7 @@ func entryLogFields(entry spireapi.Entry) []interface{} {
|
|||
adminKey, entry.Admin,
|
||||
downstreamKey, entry.Downstream,
|
||||
hintKey, entry.Hint,
|
||||
storeSVIDKey, entry.StoreSVID,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,7 +92,7 @@ func stringList(ss []string) string {
|
|||
func renderList(n int, fn func(i int, w io.StringWriter)) string {
|
||||
var builder strings.Builder
|
||||
builder.WriteRune('[')
|
||||
for i := 0; i < n; i++ {
|
||||
for i := range n {
|
||||
if i > 0 {
|
||||
builder.WriteRune(',')
|
||||
}
|
||||
|
|
|
@ -20,20 +20,19 @@ import (
|
|||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/google/uuid"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/spiffe/go-spiffe/v2/spiffeid"
|
||||
spirev1alpha1 "github.com/spiffe/spire-controller-manager/api/v1alpha1"
|
||||
"github.com/spiffe/spire-controller-manager/pkg/k8sapi"
|
||||
"github.com/spiffe/spire-controller-manager/pkg/namespace"
|
||||
"github.com/spiffe/spire-controller-manager/pkg/reconciler"
|
||||
"github.com/spiffe/spire-controller-manager/pkg/spireapi"
|
||||
|
||||
"google.golang.org/grpc/codes"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
@ -41,15 +40,42 @@ import (
|
|||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
|
||||
spirev1alpha1 "github.com/spiffe/spire-controller-manager/api/v1alpha1"
|
||||
"github.com/spiffe/spire-controller-manager/pkg/k8sapi"
|
||||
"github.com/spiffe/spire-controller-manager/pkg/metrics"
|
||||
"github.com/spiffe/spire-controller-manager/pkg/namespace"
|
||||
"github.com/spiffe/spire-controller-manager/pkg/reconciler"
|
||||
"github.com/spiffe/spire-controller-manager/pkg/spireapi"
|
||||
)
|
||||
|
||||
const (
|
||||
// joinTokenSpiffePrefix is the prefix that is the part of the parent SPIFFE ID for join token entries.
|
||||
// Ref: https://github.com/spiffe/spire/blob/v1.8.7/pkg/server/api/agent/v1/service.go#L714
|
||||
//nolint: gosec // not a credential
|
||||
joinTokenSpiffePrefix = "/spire/agent/join_token/"
|
||||
|
||||
// joinTokenSelectorType is the selector type used in the selector for join token entries.
|
||||
// Ref: https://github.com/spiffe/spire/blob/v1.8.7/pkg/server/api/agent/v1/service.go#L515
|
||||
//nolint: gosec // not a credential
|
||||
joinTokenSelectorType = "spiffe_id"
|
||||
)
|
||||
|
||||
type ReconcilerConfig struct {
|
||||
TrustDomain spiffeid.TrustDomain
|
||||
ClusterName string
|
||||
ClusterDomain string
|
||||
EntryClient spireapi.EntryClient
|
||||
K8sClient client.Client
|
||||
IgnoreNamespaces []*regexp.Regexp
|
||||
TrustDomain spiffeid.TrustDomain
|
||||
ClusterName string
|
||||
ClusterDomain string
|
||||
EntryClient spireapi.EntryClient
|
||||
K8sClient client.Client
|
||||
IgnoreNamespaces []*regexp.Regexp
|
||||
AutoPopulateDNSNames bool
|
||||
ClassName string
|
||||
WatchClassless bool
|
||||
ParentIDTemplate *template.Template
|
||||
Reconcile spirev1alpha1.ReconcileConfig
|
||||
EntryIDPrefix string
|
||||
EntryIDPrefixCleanup *string
|
||||
StaticManifestPath *string
|
||||
|
||||
// GCInterval how long to sit idle (i.e. untriggered) before doing
|
||||
// another reconcile.
|
||||
|
@ -58,7 +84,9 @@ type ReconcilerConfig struct {
|
|||
|
||||
func Reconciler(config ReconcilerConfig) reconciler.Reconciler {
|
||||
r := &entryReconciler{
|
||||
config: config,
|
||||
config: config,
|
||||
promCounter: metrics.PromCounters,
|
||||
staticManifestPath: config.StaticManifestPath,
|
||||
}
|
||||
return reconciler.New(reconciler.Config{
|
||||
Kind: "entry",
|
||||
|
@ -71,7 +99,9 @@ type entryReconciler struct {
|
|||
config ReconcilerConfig
|
||||
|
||||
unsupportedFields map[spireapi.Field]struct{}
|
||||
promCounter map[string]prometheus.Counter
|
||||
nextGetUnsupportedFields time.Time
|
||||
staticManifestPath *string
|
||||
}
|
||||
|
||||
func (r *entryReconciler) reconcile(ctx context.Context) {
|
||||
|
@ -83,7 +113,7 @@ func (r *entryReconciler) reconcile(ctx context.Context) {
|
|||
unsupportedFields := r.unsupportedFields
|
||||
|
||||
// Load current entries from SPIRE server.
|
||||
currentEntries, err := r.listEntries(ctx)
|
||||
currentEntries, deleteOnlyEntries, err := r.listEntries(ctx)
|
||||
if err != nil {
|
||||
log.Error(err, "Failed to list SPIRE entries")
|
||||
return
|
||||
|
@ -95,21 +125,27 @@ func (r *entryReconciler) reconcile(ctx context.Context) {
|
|||
state.AddCurrent(entry)
|
||||
}
|
||||
|
||||
// Load and add entry state for ClusterStaticEntries
|
||||
clusterStaticEntries, err := r.listClusterStaticEntries(ctx)
|
||||
if err != nil {
|
||||
log.Error(err, "Failed to list ClusterStaticEntries")
|
||||
return
|
||||
clusterStaticEntries := []*ClusterStaticEntry{}
|
||||
if r.config.Reconcile.ClusterStaticEntries {
|
||||
// Load and add entry state for ClusterStaticEntries
|
||||
clusterStaticEntries, err = r.listClusterStaticEntries(ctx)
|
||||
if err != nil {
|
||||
log.Error(err, "Failed to list ClusterStaticEntries")
|
||||
return
|
||||
}
|
||||
r.addClusterStaticEntryEntriesState(ctx, state, clusterStaticEntries)
|
||||
}
|
||||
r.addClusterStaticEntryEntriesState(ctx, state, clusterStaticEntries)
|
||||
|
||||
// Load and add entry state for ClusterSPIFFEIDs
|
||||
clusterSPIFFEIDs, err := r.listClusterSPIFFEIDs(ctx)
|
||||
if err != nil {
|
||||
log.Error(err, "Failed to list ClusterSPIFFEIDs")
|
||||
return
|
||||
clusterSPIFFEIDs := []*ClusterSPIFFEID{}
|
||||
if r.config.Reconcile.ClusterSPIFFEIDs {
|
||||
// Load and add entry state for ClusterSPIFFEIDs
|
||||
clusterSPIFFEIDs, err = r.listClusterSPIFFEIDs(ctx)
|
||||
if err != nil {
|
||||
log.Error(err, "Failed to list ClusterSPIFFEIDs")
|
||||
return
|
||||
}
|
||||
r.addClusterSPIFFEIDEntriesState(ctx, state, clusterSPIFFEIDs)
|
||||
}
|
||||
r.addClusterSPIFFEIDEntriesState(ctx, state, clusterSPIFFEIDs)
|
||||
|
||||
var toDelete []spireapi.Entry
|
||||
var toCreate []declaredEntry
|
||||
|
@ -132,6 +168,9 @@ func (r *entryReconciler) reconcile(ctx context.Context) {
|
|||
// drop the current entry from the list so it isn't added to the
|
||||
// "to delete" list.
|
||||
if len(s.Current) == 0 {
|
||||
if preferredEntry.Entry.ID == "" && r.config.EntryIDPrefix != "" {
|
||||
preferredEntry.Entry.ID = fmt.Sprintf("%s%s", r.config.EntryIDPrefix, uuid.New())
|
||||
}
|
||||
toCreate = append(toCreate, preferredEntry)
|
||||
} else {
|
||||
preferredEntry.Entry.ID = s.Current[0].ID
|
||||
|
@ -143,11 +182,12 @@ func (r *entryReconciler) reconcile(ctx context.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
// Any remaining current entries should be removed that aren't going
|
||||
// to be reused for the entry update.
|
||||
toDelete = append(toDelete, s.Current...)
|
||||
// Any remaining current entries that are not associated with join tokens
|
||||
// should be removed as they aren't going to be reused for the entry update.
|
||||
toDelete = append(toDelete, filterJoinTokenEntries(s.Current)...)
|
||||
}
|
||||
|
||||
toDelete = append(toDelete, deleteOnlyEntries...)
|
||||
if len(toDelete) > 0 {
|
||||
r.deleteEntries(ctx, toDelete)
|
||||
}
|
||||
|
@ -166,6 +206,9 @@ func (r *entryReconciler) reconcile(ctx context.Context) {
|
|||
continue
|
||||
}
|
||||
clusterStaticEntry.Status = clusterStaticEntry.NextStatus
|
||||
if r.config.K8sClient == nil {
|
||||
continue
|
||||
}
|
||||
if err := r.config.K8sClient.Status().Update(ctx, &clusterStaticEntry.ClusterStaticEntry); err == nil {
|
||||
log.Info("Updated status")
|
||||
} else {
|
||||
|
@ -189,6 +232,10 @@ func (r *entryReconciler) reconcile(ctx context.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
func (r *entryReconciler) reconcileClass(className string) bool {
|
||||
return (className == "" && r.config.WatchClassless) || className == r.config.ClassName
|
||||
}
|
||||
|
||||
func (r *entryReconciler) recalculateUnsupportFields(ctx context.Context, log logr.Logger) {
|
||||
unsupportedFields, err := r.getUnsupportedFields(ctx)
|
||||
if err != nil {
|
||||
|
@ -222,9 +269,43 @@ func (r *entryReconciler) recalculateUnsupportFields(ctx context.Context, log lo
|
|||
r.nextGetUnsupportedFields = time.Now().Add(10 * time.Minute)
|
||||
}
|
||||
|
||||
func (r *entryReconciler) listEntries(ctx context.Context) ([]spireapi.Entry, error) {
|
||||
func (r *entryReconciler) shouldProcessOrDeleteEntryID(entry spireapi.Entry) (bool, bool) {
|
||||
if r.config.EntryIDPrefix == "" {
|
||||
return true, false
|
||||
}
|
||||
if strings.HasPrefix(entry.ID, r.config.EntryIDPrefix) {
|
||||
return true, false
|
||||
}
|
||||
if r.config.EntryIDPrefixCleanup != nil {
|
||||
cleanupPrefix := *r.config.EntryIDPrefixCleanup
|
||||
if cleanupPrefix == "" {
|
||||
return false, !strings.Contains(entry.ID, ".")
|
||||
}
|
||||
if strings.HasPrefix(entry.ID, cleanupPrefix) {
|
||||
return false, true
|
||||
}
|
||||
}
|
||||
return false, false
|
||||
}
|
||||
|
||||
func (r *entryReconciler) listEntries(ctx context.Context) ([]spireapi.Entry, []spireapi.Entry, error) {
|
||||
// TODO: cache?
|
||||
return r.config.EntryClient.ListEntries(ctx)
|
||||
var deleteOnlyEntries []spireapi.Entry
|
||||
var currentEntries []spireapi.Entry
|
||||
tmpvals, err := r.config.EntryClient.ListEntries(ctx)
|
||||
if err != nil {
|
||||
return currentEntries, deleteOnlyEntries, err
|
||||
}
|
||||
for _, value := range tmpvals {
|
||||
proc, del := r.shouldProcessOrDeleteEntryID(value)
|
||||
if proc {
|
||||
currentEntries = append(currentEntries, value)
|
||||
}
|
||||
if del {
|
||||
deleteOnlyEntries = append(deleteOnlyEntries, value)
|
||||
}
|
||||
}
|
||||
return currentEntries, deleteOnlyEntries, nil
|
||||
}
|
||||
|
||||
func (r *entryReconciler) getUnsupportedFields(ctx context.Context) (map[spireapi.Field]struct{}, error) {
|
||||
|
@ -232,15 +313,23 @@ func (r *entryReconciler) getUnsupportedFields(ctx context.Context) (map[spireap
|
|||
}
|
||||
|
||||
func (r *entryReconciler) listClusterStaticEntries(ctx context.Context) ([]*ClusterStaticEntry, error) {
|
||||
clusterStaticEntries, err := k8sapi.ListClusterStaticEntries(ctx, r.config.K8sClient)
|
||||
var clusterStaticEntries []spirev1alpha1.ClusterStaticEntry
|
||||
var err error
|
||||
if r.config.K8sClient != nil {
|
||||
clusterStaticEntries, err = k8sapi.ListClusterStaticEntries(ctx, r.config.K8sClient)
|
||||
} else {
|
||||
clusterStaticEntries, err = spirev1alpha1.ListClusterStaticEntries(ctx, *r.staticManifestPath)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]*ClusterStaticEntry, 0, len(clusterStaticEntries))
|
||||
for _, clusterStaticEntry := range clusterStaticEntries {
|
||||
out = append(out, &ClusterStaticEntry{
|
||||
ClusterStaticEntry: clusterStaticEntry,
|
||||
})
|
||||
if r.reconcileClass(clusterStaticEntry.Spec.ClassName) {
|
||||
out = append(out, &ClusterStaticEntry{
|
||||
ClusterStaticEntry: clusterStaticEntry,
|
||||
})
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
@ -252,9 +341,11 @@ func (r *entryReconciler) listClusterSPIFFEIDs(ctx context.Context) ([]*ClusterS
|
|||
}
|
||||
out := make([]*ClusterSPIFFEID, 0, len(clusterSPIFFEIDs))
|
||||
for _, clusterSPIFFEID := range clusterSPIFFEIDs {
|
||||
out = append(out, &ClusterSPIFFEID{
|
||||
ClusterSPIFFEID: clusterSPIFFEID,
|
||||
})
|
||||
if r.reconcileClass(clusterSPIFFEID.Spec.ClassName) {
|
||||
out = append(out, &ClusterSPIFFEID{
|
||||
ClusterSPIFFEID: clusterSPIFFEID,
|
||||
})
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
@ -275,6 +366,7 @@ func (r *entryReconciler) addClusterStaticEntryEntriesState(ctx context.Context,
|
|||
if err != nil {
|
||||
log.Error(err, "Failed to render ClusterStaticEntry")
|
||||
clusterStaticEntry.NextStatus.Rendered = false
|
||||
r.promCounter[metrics.StaticEntryFailures].Add(1)
|
||||
continue
|
||||
}
|
||||
clusterStaticEntry.NextStatus.Rendered = true
|
||||
|
@ -284,6 +376,17 @@ func (r *entryReconciler) addClusterStaticEntryEntriesState(ctx context.Context,
|
|||
|
||||
func (r *entryReconciler) addClusterSPIFFEIDEntriesState(ctx context.Context, state entriesState, clusterSPIFFEIDs []*ClusterSPIFFEID) {
|
||||
log := log.FromContext(ctx)
|
||||
podsWithNonFallbackApplied := make(map[types.UID]struct{})
|
||||
// Process all the fallback clusterSPIFFEIDs last.
|
||||
slices.SortStableFunc(clusterSPIFFEIDs, func(x, y *ClusterSPIFFEID) int {
|
||||
if x.Spec.Fallback == y.Spec.Fallback {
|
||||
return 0
|
||||
}
|
||||
if x.Spec.Fallback {
|
||||
return 1
|
||||
}
|
||||
return -1
|
||||
})
|
||||
for _, clusterSPIFFEID := range clusterSPIFFEIDs {
|
||||
log := log.WithValues(clusterSPIFFEIDLogKey, objectName(clusterSPIFFEID))
|
||||
|
||||
|
@ -325,6 +428,9 @@ func (r *entryReconciler) addClusterSPIFFEIDEntriesState(ctx context.Context, st
|
|||
clusterSPIFFEID.NextStatus.Stats.PodsSelected += len(pods)
|
||||
for i := range pods {
|
||||
log := log.WithValues(podLogKey, objectName(&pods[i]))
|
||||
if _, ok := podsWithNonFallbackApplied[pods[i].UID]; ok && clusterSPIFFEID.Spec.Fallback {
|
||||
continue
|
||||
}
|
||||
|
||||
entry, err := r.renderPodEntry(ctx, spec, &pods[i])
|
||||
switch {
|
||||
|
@ -335,6 +441,9 @@ func (r *entryReconciler) addClusterSPIFFEIDEntriesState(ctx context.Context, st
|
|||
// renderPodEntry will return a nil entry if requisite k8s
|
||||
// objects disappeared from underneath.
|
||||
state.AddDeclared(*entry, clusterSPIFFEID)
|
||||
if !clusterSPIFFEID.Spec.Fallback {
|
||||
podsWithNonFallbackApplied[pods[i].UID] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -348,7 +457,13 @@ func (r *entryReconciler) renderPodEntry(ctx context.Context, spec *spirev1alpha
|
|||
if err := r.config.K8sClient.Get(ctx, types.NamespacedName{Name: pod.Spec.NodeName}, node); err != nil {
|
||||
return nil, client.IgnoreNotFound(err)
|
||||
}
|
||||
return renderPodEntry(spec, node, pod, r.config.TrustDomain, r.config.ClusterName, r.config.ClusterDomain)
|
||||
endpointsList := &corev1.EndpointsList{}
|
||||
if spec.AutoPopulateDNSNames {
|
||||
if err := r.config.K8sClient.List(ctx, endpointsList, client.InNamespace(pod.Namespace), client.MatchingFields{reconciler.EndpointUID: string(pod.UID)}); err != nil && !apierrors.IsNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return renderPodEntry(spec, node, pod, endpointsList, r.config.TrustDomain, r.config.ClusterName, r.config.ClusterDomain, r.config.ParentIDTemplate)
|
||||
}
|
||||
|
||||
func (r *entryReconciler) createEntries(ctx context.Context, declaredEntries []declaredEntry) {
|
||||
|
@ -554,6 +669,11 @@ func getOutdatedEntryFields(newEntry, oldEntry spireapi.Entry, unsupportedFields
|
|||
outdated = append(outdated, spireapi.HintField)
|
||||
}
|
||||
}
|
||||
if oldEntry.StoreSVID != newEntry.StoreSVID {
|
||||
if _, ok := unsupportedFields[spireapi.StoreSVIDField]; !ok {
|
||||
outdated = append(outdated, spireapi.StoreSVIDField)
|
||||
}
|
||||
}
|
||||
|
||||
return outdated
|
||||
}
|
||||
|
@ -611,3 +731,34 @@ func idsFromEntries(entries []spireapi.Entry) []string {
|
|||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
// filterJoinTokenEntries filters out entries that correspond to join tokens.
|
||||
func filterJoinTokenEntries(entries []spireapi.Entry) []spireapi.Entry {
|
||||
if len(entries) == 0 {
|
||||
return entries
|
||||
}
|
||||
filteredEntries := make([]spireapi.Entry, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
if isJoinTokenEntry(entry) {
|
||||
continue
|
||||
}
|
||||
filteredEntries = append(filteredEntries, entry)
|
||||
}
|
||||
return filteredEntries
|
||||
}
|
||||
|
||||
// isJoinTokenEntry returns true if the entry corresponds to a join token.
|
||||
// For an entry to correspond to a join token, both the following conditions must be true:
|
||||
// 1. The path of the parent ID of the entry must begin with "/spire/agent/join_token/".
|
||||
// 2. The entry must contain a selector of type "spiffe_id".
|
||||
func isJoinTokenEntry(entry spireapi.Entry) bool {
|
||||
if !strings.HasPrefix(entry.ParentID.Path(), joinTokenSpiffePrefix) {
|
||||
return false
|
||||
}
|
||||
for _, selector := range entry.Selectors {
|
||||
if selector.Type == joinTokenSelectorType {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -75,3 +75,51 @@ func TestMakeEntryKey(t *testing.T) {
|
|||
require.Equal(t, makeEntryKey(a), makeEntryKey(b))
|
||||
})
|
||||
}
|
||||
|
||||
func TestFilterJoinTokenEntries(t *testing.T) {
|
||||
id1 := spiffeid.RequireFromString("spiffe://domain.test/1")
|
||||
id2 := spiffeid.RequireFromString("spiffe://domain.test/2")
|
||||
id3 := spiffeid.RequireFromString("spiffe://domain.test/3")
|
||||
idJoinToken := spiffeid.RequireFromString("spiffe://domain.test/spire/agent/join_token/717290d1-6e81-40cc-b9c4-1416f8c30cfd")
|
||||
s1 := []spireapi.Selector{{Type: "A", Value: "A"}, {Type: "B", Value: "B"}}
|
||||
s2 := []spireapi.Selector{{Type: "B", Value: "B"}, {Type: "A", Value: "A"}}
|
||||
sJoinToken := []spireapi.Selector{{Type: "spiffe_id", Value: "A"}}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
entries []spireapi.Entry
|
||||
expected []spireapi.Entry
|
||||
}{
|
||||
{
|
||||
name: "no join token entries",
|
||||
entries: []spireapi.Entry{
|
||||
{ID: "1", ParentID: id1, SPIFFEID: id2, Selectors: s1},
|
||||
{ID: "2", ParentID: id1, SPIFFEID: id3, Selectors: s2},
|
||||
},
|
||||
expected: []spireapi.Entry{
|
||||
{ID: "1", ParentID: id1, SPIFFEID: id2, Selectors: s1},
|
||||
{ID: "2", ParentID: id1, SPIFFEID: id3, Selectors: s2},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with join token entries",
|
||||
entries: []spireapi.Entry{
|
||||
{ID: "1", ParentID: id1, SPIFFEID: id2, Selectors: s1},
|
||||
{ID: "2", ParentID: id1, SPIFFEID: id3, Selectors: s2},
|
||||
{ID: "3", ParentID: idJoinToken, SPIFFEID: id3, Selectors: sJoinToken},
|
||||
{ID: "4", ParentID: idJoinToken, SPIFFEID: id2, Selectors: sJoinToken},
|
||||
},
|
||||
expected: []spireapi.Entry{
|
||||
{ID: "1", ParentID: id1, SPIFFEID: id2, Selectors: s1},
|
||||
{ID: "2", ParentID: id1, SPIFFEID: id3, Selectors: s2},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
actual := filterJoinTokenEntries(tc.entries)
|
||||
require.Equal(t, tc.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,8 +32,11 @@ import (
|
|||
)
|
||||
|
||||
type ReconcilerConfig struct {
|
||||
TrustDomainClient spireapi.TrustDomainClient
|
||||
K8sClient client.Client
|
||||
TrustDomainClient spireapi.TrustDomainClient
|
||||
K8sClient client.Client
|
||||
ClassName string
|
||||
WatchClassless bool
|
||||
StaticManifestPath *string
|
||||
|
||||
// GCInterval how long to sit idle (i.e. untriggered) before doing
|
||||
// another reconcile.
|
||||
|
@ -44,23 +47,29 @@ func Reconciler(config ReconcilerConfig) reconciler.Reconciler {
|
|||
return reconciler.New(reconciler.Config{
|
||||
Kind: "federation relationship",
|
||||
Reconcile: func(ctx context.Context) {
|
||||
Reconcile(ctx, config.TrustDomainClient, config.K8sClient)
|
||||
Reconcile(ctx, config.TrustDomainClient, config.K8sClient, config.ClassName, config.WatchClassless, config.StaticManifestPath)
|
||||
},
|
||||
GCInterval: config.GCInterval,
|
||||
})
|
||||
}
|
||||
|
||||
func Reconcile(ctx context.Context, trustDomainClient spireapi.TrustDomainClient, k8sClient client.Client) {
|
||||
func Reconcile(ctx context.Context, trustDomainClient spireapi.TrustDomainClient, k8sClient client.Client, className string, watchClassless bool, staticManifestPath *string) {
|
||||
r := &federationRelationshipReconciler{
|
||||
trustDomainClient: trustDomainClient,
|
||||
k8sClient: k8sClient,
|
||||
trustDomainClient: trustDomainClient,
|
||||
k8sClient: k8sClient,
|
||||
className: className,
|
||||
watchClassless: watchClassless,
|
||||
staticManifestPath: staticManifestPath,
|
||||
}
|
||||
r.reconcile(ctx)
|
||||
}
|
||||
|
||||
type federationRelationshipReconciler struct {
|
||||
trustDomainClient spireapi.TrustDomainClient
|
||||
k8sClient client.Client
|
||||
trustDomainClient spireapi.TrustDomainClient
|
||||
k8sClient client.Client
|
||||
className string
|
||||
watchClassless bool
|
||||
staticManifestPath *string
|
||||
}
|
||||
|
||||
func (r *federationRelationshipReconciler) reconcile(ctx context.Context) {
|
||||
|
@ -110,6 +119,10 @@ func (r *federationRelationshipReconciler) reconcile(ctx context.Context) {
|
|||
// TODO: Status updates
|
||||
}
|
||||
|
||||
func (r *federationRelationshipReconciler) reconcileClass(className string) bool {
|
||||
return (className == "" && r.watchClassless) || className == r.className
|
||||
}
|
||||
|
||||
func (r *federationRelationshipReconciler) listFederationRelationships(ctx context.Context) (map[spiffeid.TrustDomain]spireapi.FederationRelationship, error) {
|
||||
federationRelationships, err := r.trustDomainClient.ListFederationRelationships(ctx)
|
||||
if err != nil {
|
||||
|
@ -125,7 +138,13 @@ func (r *federationRelationshipReconciler) listFederationRelationships(ctx conte
|
|||
func (r *federationRelationshipReconciler) listClusterFederatedTrustDomains(ctx context.Context) (map[spiffeid.TrustDomain]*clusterFederatedTrustDomainState, error) {
|
||||
log := log.FromContext(ctx)
|
||||
|
||||
clusterFederatedTrustDomains, err := k8sapi.ListClusterFederatedTrustDomains(ctx, r.k8sClient)
|
||||
var clusterFederatedTrustDomains []spirev1alpha1.ClusterFederatedTrustDomain
|
||||
var err error
|
||||
if r.k8sClient != nil {
|
||||
clusterFederatedTrustDomains, err = k8sapi.ListClusterFederatedTrustDomains(ctx, r.k8sClient)
|
||||
} else {
|
||||
clusterFederatedTrustDomains, err = spirev1alpha1.ListClusterFederatedTrustDomains(ctx, *r.staticManifestPath)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -138,6 +157,9 @@ func (r *federationRelationshipReconciler) listClusterFederatedTrustDomains(ctx
|
|||
|
||||
out := make(map[spiffeid.TrustDomain]*clusterFederatedTrustDomainState, len(clusterFederatedTrustDomains))
|
||||
for i := range clusterFederatedTrustDomains {
|
||||
if !(r.reconcileClass(clusterFederatedTrustDomains[i].Spec.ClassName)) {
|
||||
continue
|
||||
}
|
||||
log := log.WithValues(clusterFederatedTrustDomainLogKey, objectName(&clusterFederatedTrustDomains[i]))
|
||||
|
||||
federationRelationship, err := spirev1alpha1.ParseClusterFederatedTrustDomainSpec(&clusterFederatedTrustDomains[i].Spec)
|
||||
|
|
|
@ -191,7 +191,7 @@ func TestReconcile(t *testing.T) {
|
|||
ctx := log.IntoContext(context.Background(), logrtesting.NewTestLogger(t))
|
||||
|
||||
k8sClient := k8stest.NewClientBuilder(t).WithRuntimeObjects(tt.withObjects...).Build()
|
||||
spirefederationrelationship.Reconcile(ctx, tdc, k8sClient)
|
||||
spirefederationrelationship.Reconcile(ctx, tdc, k8sClient, "", false, nil)
|
||||
assert.Equal(t, tt.expectFRs, tdc.getFederationRelationships())
|
||||
})
|
||||
}
|
||||
|
@ -217,14 +217,14 @@ func newTrustDomainClient() *trustDomainClient {
|
|||
}
|
||||
}
|
||||
|
||||
func (t *trustDomainClient) ListFederationRelationships(ctx context.Context) ([]spireapi.FederationRelationship, error) {
|
||||
func (t *trustDomainClient) ListFederationRelationships(context.Context) ([]spireapi.FederationRelationship, error) {
|
||||
if t.listError != nil {
|
||||
return nil, t.listError
|
||||
}
|
||||
return t.getFederationRelationships(), nil
|
||||
}
|
||||
|
||||
func (t *trustDomainClient) CreateFederationRelationships(ctx context.Context, federationRelationships []spireapi.FederationRelationship) ([]spireapi.Status, error) {
|
||||
func (t *trustDomainClient) CreateFederationRelationships(_ context.Context, federationRelationships []spireapi.FederationRelationship) ([]spireapi.Status, error) {
|
||||
if t.createError != nil {
|
||||
return nil, t.createError
|
||||
}
|
||||
|
@ -244,7 +244,7 @@ func (t *trustDomainClient) CreateFederationRelationships(ctx context.Context, f
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func (t *trustDomainClient) UpdateFederationRelationships(ctx context.Context, federationRelationships []spireapi.FederationRelationship) ([]spireapi.Status, error) {
|
||||
func (t *trustDomainClient) UpdateFederationRelationships(_ context.Context, federationRelationships []spireapi.FederationRelationship) ([]spireapi.Status, error) {
|
||||
if t.updateError != nil {
|
||||
return nil, t.updateError
|
||||
}
|
||||
|
@ -264,7 +264,7 @@ func (t *trustDomainClient) UpdateFederationRelationships(ctx context.Context, f
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func (t *trustDomainClient) DeleteFederationRelationships(ctx context.Context, tds []spiffeid.TrustDomain) ([]spireapi.Status, error) {
|
||||
func (t *trustDomainClient) DeleteFederationRelationships(_ context.Context, tds []spiffeid.TrustDomain) ([]spireapi.Status, error) {
|
||||
if t.deleteError != nil {
|
||||
return nil, t.deleteError
|
||||
}
|
||||
|
|
|
@ -169,6 +169,10 @@ func (m *Manager) Start(ctx context.Context) error {
|
|||
}
|
||||
}
|
||||
|
||||
func (m *Manager) NeedLeaderElection() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *Manager) mintX509SVIDIfNeeded(ctx context.Context, store cache.Store) error {
|
||||
log := log.FromContext(ctx)
|
||||
|
||||
|
@ -364,7 +368,7 @@ func dnsNamesEqual(a, b []string) bool {
|
|||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(a); i++ {
|
||||
for i := range a {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
|
@ -383,8 +387,8 @@ func startInformer(ctx context.Context, config Config) (cache.Store, chan struct
|
|||
}
|
||||
|
||||
log := log.FromContext(ctx)
|
||||
store, controller := cache.NewInformer(
|
||||
&cache.ListWatch{
|
||||
store, controller := cache.NewInformerWithOptions(cache.InformerOptions{
|
||||
ListerWatcher: &cache.ListWatch{
|
||||
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
|
||||
return config.WebhookClient.List(ctx, options)
|
||||
},
|
||||
|
@ -392,29 +396,29 @@ func startInformer(ctx context.Context, config Config) (cache.Store, chan struct
|
|||
return config.WebhookClient.Watch(ctx, options)
|
||||
},
|
||||
},
|
||||
&admissionregistrationv1.ValidatingWebhookConfiguration{},
|
||||
time.Hour,
|
||||
cache.FilteringResourceEventHandler{
|
||||
ObjectType: &admissionregistrationv1.ValidatingWebhookConfiguration{},
|
||||
ResyncPeriod: time.Hour,
|
||||
Handler: cache.FilteringResourceEventHandler{
|
||||
FilterFunc: func(obj interface{}) bool {
|
||||
o, ok := obj.(*admissionregistrationv1.ValidatingWebhookConfiguration)
|
||||
return ok && o.Name == config.WebhookName
|
||||
},
|
||||
Handler: cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
AddFunc: func(_ interface{}) {
|
||||
log.Info("Received webhook added event")
|
||||
notify()
|
||||
},
|
||||
UpdateFunc: func(oldObj, newObj interface{}) {
|
||||
UpdateFunc: func(_, _ interface{}) {
|
||||
log.Info("Received webhook updated event")
|
||||
notify()
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
DeleteFunc: func(_ interface{}) {
|
||||
log.Info("Received webhook deleted event")
|
||||
notify()
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
wg := new(sync.WaitGroup)
|
||||
wg.Add(1)
|
||||
|
|
Loading…
Reference in New Issue