Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions config/manager/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ spec:
path: /healthz
readinessProbe:
httpGet:
port: http
path: /
path: /readyz
port: healthz
resources:
limits:
cpu: 1000m
Expand Down
32 changes: 32 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
|---------------------------------------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `--artifact-retention-records` | int | The maximum number of artifacts to be kept in storage after a garbage collection. (default 2) |
| `--artifact-retention-ttl` | duration | The duration of time that artifacts from previous reconciliations will be kept in storage before being garbage collected. (default 1m0s) |
| `--authorized-artifact-client-spiffe-id` | strings | SPIFFE ID authorized to fetch artifacts from the artifact server. Can be repeated. When set, the artifact server uses SPIFFE mTLS. |
| `--authorized-event-server-spiffe-id` | strings | SPIFFE ID of an event server authorized to receive events from source-controller. Can be repeated. When set, the event recorder uses SPIFFE mTLS. |
| `--concurrent` | int | The number of concurrent reconciles per controller. (default 2) |
| `--default-service-account` | string | Default service account to use for workload identity when not specified in resources. |
| `--enable-leader-election` | boolean | Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager. |
Expand Down Expand Up @@ -39,6 +41,36 @@
| `--watch-label-selector` | string | Watch for resources with matching labels e.g. 'sharding.fluxcd.io/key=shard1'. |
| `--feature-gates` | mapStringBool | A comma separated list of key=value pairs defining the state of experimental features. |

### SPIFFE mTLS

When at least one of the repeatable flags `--authorized-artifact-client-spiffe-id`
or `--authorized-event-server-spiffe-id` is set, source-controller acquires an
X.509-SVID from the SPIFFE Workload API. The `SPIFFE_ENDPOINT_SOCKET` environment
variable must point to the Workload API socket.

- `--authorized-artifact-client-spiffe-id` authorizes the given SPIFFE IDs to
fetch artifacts from the artifact server. When set, the artifact server uses
mTLS.
- `--authorized-event-server-spiffe-id` authorizes the given SPIFFE IDs of
event servers to which source-controller sends events. When set, the event
recorder uses mTLS.

Each traffic type is protected independently. SPIFFE mTLS is transparent to the
Flux API: the scheme of `.status.artifact.url` does not change.

Each flag value is interpreted as follows:

- `spiffe://<trust domain>/<path>` authorizes exactly that SPIFFE ID.
- `spiffe://<trust domain>/<path>/` (with a trailing slash) authorizes any
SPIFFE ID with that path prefix.
- `spiffe://<trust domain>` (without a path) authorizes any SPIFFE ID in that
trust domain.

Whenever `<trust domain>` equals the special value `self`, source-controller
understands it as the trust domain of the controller's own X509-SVID.

Invalid values cause source-controller to fail at startup.

### Feature Gates

| Name | Default Value | Description |
Expand Down
15 changes: 8 additions & 7 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,21 @@ require (
github.com/docker/go-units v0.5.0
github.com/elazarl/goproxy v1.8.4
github.com/fluxcd/cli-utils v1.4.0
github.com/fluxcd/pkg/apis/crypto v0.4.1-0.20261020182949-c5194b17fdad
github.com/fluxcd/pkg/apis/crypto v0.4.1-0.20261022125904-05e84321fa52
github.com/fluxcd/pkg/apis/event v0.32.0
github.com/fluxcd/pkg/apis/meta v1.36.1-0.20261020182949-c5194b17fdad
github.com/fluxcd/pkg/artifact v0.25.1-0.20261020182949-c5194b17fdad
github.com/fluxcd/pkg/auth v0.63.1-0.20261020182949-c5194b17fdad
github.com/fluxcd/pkg/cache v0.17.1-0.20261020182949-c5194b17fdad
github.com/fluxcd/pkg/apis/meta v1.36.1-0.20261022125904-05e84321fa52
github.com/fluxcd/pkg/artifact v0.25.1-0.20261022125904-05e84321fa52
github.com/fluxcd/pkg/auth v0.63.1-0.20261022125904-05e84321fa52
github.com/fluxcd/pkg/cache v0.17.1-0.20261022125904-05e84321fa52
github.com/fluxcd/pkg/git v0.55.0
github.com/fluxcd/pkg/gittestserver v0.32.0
github.com/fluxcd/pkg/helmtestserver v0.44.0
github.com/fluxcd/pkg/http/transport v0.9.0
github.com/fluxcd/pkg/masktoken v0.10.0
github.com/fluxcd/pkg/oci v0.72.0
github.com/fluxcd/pkg/runtime v0.120.1-0.20261020182949-c5194b17fdad
github.com/fluxcd/pkg/runtime v0.120.1-0.20261022125904-05e84321fa52
github.com/fluxcd/pkg/sourceignore v0.21.0
github.com/fluxcd/pkg/spiffe v0.0.0-20261022125904-05e84321fa52
github.com/fluxcd/pkg/ssh v0.28.0
github.com/fluxcd/pkg/tar v1.3.0
github.com/fluxcd/pkg/testserver v0.15.0
Expand Down Expand Up @@ -64,6 +65,7 @@ require (
github.com/sigstore/sigstore-go v1.2.1
github.com/sirupsen/logrus v1.10.2
github.com/spf13/pflag v1.0.10
github.com/spiffe/go-spiffe/v2 v2.9.0
golang.org/x/crypto v0.58.0
golang.org/x/sync v0.24.0
google.golang.org/api v0.297.0
Expand Down Expand Up @@ -327,7 +329,6 @@ require (
github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/cobra v1.10.2 // indirect
github.com/spf13/viper v1.21.0 // indirect
github.com/spiffe/go-spiffe/v2 v2.9.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 // indirect
Expand Down
26 changes: 14 additions & 12 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -350,18 +350,18 @@ github.com/fluxcd/gitkit v0.6.0 h1:iNg5LTx6ePo+Pl0ZwqHTAkhbUHxGVSY3YCxCdw7VIFg=
github.com/fluxcd/gitkit v0.6.0/go.mod h1:svOHuKi0fO9HoawdK4HfHAJJseZDHHjk7I3ihnCIqNo=
github.com/fluxcd/pkg/apis/acl v0.12.0 h1:UWpZDqKxpKDW38wdXIMHV8Uv3POPu2vUrra0JH1frH0=
github.com/fluxcd/pkg/apis/acl v0.12.0/go.mod h1:PJrGIu5Bw/6/siO95uhS+pi5oKSU+gVJqHqsPHb8Kok=
github.com/fluxcd/pkg/apis/crypto v0.4.1-0.20261020182949-c5194b17fdad h1:SAzeZibDaqyyq7CaccwDEySCcsOMGGuHmEN28LcRPvM=
github.com/fluxcd/pkg/apis/crypto v0.4.1-0.20261020182949-c5194b17fdad/go.mod h1:bEwkDgBjLXMVJ2BwmVbbj32PkirmNSicabgl2Jvw7Dk=
github.com/fluxcd/pkg/apis/crypto v0.4.1-0.20261022125904-05e84321fa52 h1:YkDjmLFe0+RhzlFTwHgoO6xLpPYuhoiqQlMo+Qj2tW0=
github.com/fluxcd/pkg/apis/crypto v0.4.1-0.20261022125904-05e84321fa52/go.mod h1:bEwkDgBjLXMVJ2BwmVbbj32PkirmNSicabgl2Jvw7Dk=
github.com/fluxcd/pkg/apis/event v0.32.0 h1:3zyjdUspwQN+EQJPlUXwCvCMbeqtB+azlNmeenAcbuE=
github.com/fluxcd/pkg/apis/event v0.32.0/go.mod h1:ALma8dTqo+NbI+z4BawSzt0pAPjetCSNNUSSS/9p0VU=
github.com/fluxcd/pkg/apis/meta v1.36.1-0.20261020182949-c5194b17fdad h1:p/W9d+gf9FuNNmiwUZJT7ReGzTzwuYLUago5szqkq5I=
github.com/fluxcd/pkg/apis/meta v1.36.1-0.20261020182949-c5194b17fdad/go.mod h1:1MLvK2Xu3UdTKBzg+Q1Oj7H9PHeiKsUB1UW3uwxAwiM=
github.com/fluxcd/pkg/artifact v0.25.1-0.20261020182949-c5194b17fdad h1:r1anWXtWivFjkI6F0FDSHPnLw2dSMXYQpVpu+Esq+nc=
github.com/fluxcd/pkg/artifact v0.25.1-0.20261020182949-c5194b17fdad/go.mod h1:9wKz9vfH7zuhteNxQkGw9l7sCN1xW8YZttEWNN8M/L0=
github.com/fluxcd/pkg/auth v0.63.1-0.20261020182949-c5194b17fdad h1:lUjY+hEXSYG8RdJhuNYl5mjkrnLpwuG/+A+iSybCRlE=
github.com/fluxcd/pkg/auth v0.63.1-0.20261020182949-c5194b17fdad/go.mod h1:gWbB4MzuHWalVyI0o4lE84aINidor9+S63ithckm+94=
github.com/fluxcd/pkg/cache v0.17.1-0.20261020182949-c5194b17fdad h1:Lf7S9vl776ZW7SDxdQtZcEu1vjWroZS86ePd8gost4M=
github.com/fluxcd/pkg/cache v0.17.1-0.20261020182949-c5194b17fdad/go.mod h1:OW3fncHhPnPZFZav5FdEa//cyMHSndI0JS+IM69LXOU=
github.com/fluxcd/pkg/apis/meta v1.36.1-0.20261022125904-05e84321fa52 h1:vKtcpfELWz1l2eOQjWeEbCJZMNH29W4Fwa3EPM37zW0=
github.com/fluxcd/pkg/apis/meta v1.36.1-0.20261022125904-05e84321fa52/go.mod h1:1MLvK2Xu3UdTKBzg+Q1Oj7H9PHeiKsUB1UW3uwxAwiM=
github.com/fluxcd/pkg/artifact v0.25.1-0.20261022125904-05e84321fa52 h1:xUbaB4s9PtJ771fZirtqH8GDg3tnZ8qtTYQup322K4k=
github.com/fluxcd/pkg/artifact v0.25.1-0.20261022125904-05e84321fa52/go.mod h1:9wKz9vfH7zuhteNxQkGw9l7sCN1xW8YZttEWNN8M/L0=
github.com/fluxcd/pkg/auth v0.63.1-0.20261022125904-05e84321fa52 h1:QoZGEf9JonMNcEAacH8EGtSq55dplp18TTSH//M2r7o=
github.com/fluxcd/pkg/auth v0.63.1-0.20261022125904-05e84321fa52/go.mod h1:gWbB4MzuHWalVyI0o4lE84aINidor9+S63ithckm+94=
github.com/fluxcd/pkg/cache v0.17.1-0.20261022125904-05e84321fa52 h1:9ZSGSkmi4OxVCSj9Uzrt6E1Ph0ypPUajsc76pYqUg2s=
github.com/fluxcd/pkg/cache v0.17.1-0.20261022125904-05e84321fa52/go.mod h1:OW3fncHhPnPZFZav5FdEa//cyMHSndI0JS+IM69LXOU=
github.com/fluxcd/pkg/git v0.55.0 h1:jMdW0uAYuzcwIL5rJq5pmDd+jSrgOe6eIOZfdrcAvO8=
github.com/fluxcd/pkg/git v0.55.0/go.mod h1:s9AK9ZEG9CtAgkcX4QhQphLsaDBTje32tT5V3knpj1I=
github.com/fluxcd/pkg/gittestserver v0.32.0 h1:y1wbnEaOXpEYqAxGMxcsXZTH6W4QjFyTj6m1d1ytjY0=
Expand All @@ -376,10 +376,12 @@ github.com/fluxcd/pkg/masktoken v0.10.0 h1:jY9tuBIS1T7z9F8nJc9ozT0QrI391Se9vMu0e
github.com/fluxcd/pkg/masktoken v0.10.0/go.mod h1:zOCZp3trYmD5/csLOizvIGTi4gn76nnkTFu+vKOJufo=
github.com/fluxcd/pkg/oci v0.72.0 h1:nUTGHTd/2qq2yT66anHfXiwE19uJl6VL36zm74iFwbU=
github.com/fluxcd/pkg/oci v0.72.0/go.mod h1:5ngbB6SDvo6daD29HZtBbC6CcnHxuI+5JJR/YqtZhKM=
github.com/fluxcd/pkg/runtime v0.120.1-0.20261020182949-c5194b17fdad h1:AcQzxznBRZyRthcVgFjPV49uLfu7eDOoHO1B5WyRsO8=
github.com/fluxcd/pkg/runtime v0.120.1-0.20261020182949-c5194b17fdad/go.mod h1:Cw0CQ/tVKS9lgJoyKVouZr1qVnoJcYnSHw3QDzIOOjA=
github.com/fluxcd/pkg/runtime v0.120.1-0.20261022125904-05e84321fa52 h1:UD7MGD096C02MqQFDujvSGktyuDhYUnvQhjBePw3bLk=
github.com/fluxcd/pkg/runtime v0.120.1-0.20261022125904-05e84321fa52/go.mod h1:Cw0CQ/tVKS9lgJoyKVouZr1qVnoJcYnSHw3QDzIOOjA=
github.com/fluxcd/pkg/sourceignore v0.21.0 h1:p65j81RzXAeL4KJKgbkFRou+xiA0841CUdAsOCSAb5o=
github.com/fluxcd/pkg/sourceignore v0.21.0/go.mod h1:N9NFuxbEMm+UPtP9t+Oyx5LEs0uHu7W+WVPmdlIbCeY=
github.com/fluxcd/pkg/spiffe v0.0.0-20261022125904-05e84321fa52 h1:nl8eRotsfHEZS7m7SwZbZidq8F/MGj3yxyH5Vh6bas4=
github.com/fluxcd/pkg/spiffe v0.0.0-20261022125904-05e84321fa52/go.mod h1:tZ+T8ljAM9ZE0VriHSWM4vfvhlq5zv5bNcPKqwZK1lk=
github.com/fluxcd/pkg/ssh v0.28.0 h1:DbqDtvJGKzrSfXfB8Hi9T2mbTnNBodijpRZRfxx59wE=
github.com/fluxcd/pkg/ssh v0.28.0/go.mod h1:FyaNc49GQcFIHdyZzwmG/9UApGaD5nKXT6CtzWAsaE0=
github.com/fluxcd/pkg/tar v1.3.0 h1:PbL5jNN5q/F6nXA8aCH+msM75BS6lSnPX2RNd48f8ls=
Expand Down
98 changes: 74 additions & 24 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ limitations under the License.
package main

import (
"crypto/tls"
"errors"
"fmt"
"os"
"time"

flag "github.com/spf13/pflag"
"github.com/spiffe/go-spiffe/v2/workloadapi"
"helm.sh/helm/v4/pkg/getter"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
Expand Down Expand Up @@ -54,6 +56,7 @@ import (
"github.com/fluxcd/pkg/runtime/metrics"
"github.com/fluxcd/pkg/runtime/pprof"
"github.com/fluxcd/pkg/runtime/probes"
fluxspiffe "github.com/fluxcd/pkg/spiffe"

sourcev1 "github.com/fluxcd/source-controller/api/v1"

Expand Down Expand Up @@ -96,27 +99,29 @@ func main() {
)

var (
metricsAddr string
eventsAddr string
healthAddr string
concurrent int
requeueDependency time.Duration
helmIndexLimit int64
helmChartLimit int64
helmChartFileLimit int64
artifactOptions artcfg.Options
clientOptions client.Options
logOptions logger.Options
leaderElectionOptions leaderelection.Options
rateLimiterOptions helper.RateLimiterOptions
featureGates feathelper.FeatureGates
watchOptions helper.WatchOptions
intervalJitterOptions jitter.IntervalOptions
helmCacheMaxSize int
helmCacheTTL string
helmCachePurgeInterval string
tokenCacheOptions pkgcache.TokenFlags
defaultServiceAccount string
metricsAddr string
eventsAddr string
healthAddr string
concurrent int
requeueDependency time.Duration
helmIndexLimit int64
helmChartLimit int64
helmChartFileLimit int64
artifactOptions artcfg.Options
clientOptions client.Options
logOptions logger.Options
leaderElectionOptions leaderelection.Options
rateLimiterOptions helper.RateLimiterOptions
featureGates feathelper.FeatureGates
watchOptions helper.WatchOptions
intervalJitterOptions jitter.IntervalOptions
helmCacheMaxSize int
helmCacheTTL string
helmCachePurgeInterval string
tokenCacheOptions pkgcache.TokenFlags
defaultServiceAccount string
authorizedArtifactClients []string
authorizedEventServers []string
)

flag.StringVar(&metricsAddr, "metrics-addr", envOrDefault("METRICS_ADDR", ":8080"),
Expand Down Expand Up @@ -145,6 +150,10 @@ func main() {
"The list of hostkey algorithms to use for ssh connections, arranged from most preferred to the least.")
flag.StringVar(&defaultServiceAccount, auth.ControllerFlagDefaultServiceAccount,
"", "Default service account to use for workload identity when not specified in resources.")
flag.StringArrayVar(&authorizedArtifactClients, fluxspiffe.ControllerFlagAuthorizedArtifactClientSPIFFEID, nil,
"SPIFFE ID authorized to fetch artifacts from the artifact server. Can be repeated.")
flag.StringArrayVar(&authorizedEventServers, fluxspiffe.ControllerFlagAuthorizedEventServerSPIFFEID, nil,
"SPIFFE ID of an event server authorized to receive events. Can be repeated.")

artifactOptions.BindFlags(flag.CommandLine)
clientOptions.BindFlags(flag.CommandLine)
Expand All @@ -160,6 +169,8 @@ func main() {

logger.SetLogger(logger.NewLogger(logOptions))

ctx := ctrl.SetupSignalHandler()

if err := featureGates.WithLogger(setupLog).SupportedFeatures(features.FeatureGates()); err != nil {
setupLog.Error(err, "unable to load feature gates")
os.Exit(1)
Expand All @@ -186,11 +197,52 @@ func main() {
mgr := mustSetupManager(metricsAddr, healthAddr, concurrent, watchOptions, clientOptions, leaderElectionOptions)

probes.SetupChecks(mgr, setupLog)
if err := mgr.AddReadyzCheck("leader-election", probes.LeaderElectedReadyz(mgr.Elected())); err != nil {
setupLog.Error(err, "unable to create leader election readiness check")
os.Exit(1)
}

metrics := helper.NewMetrics(mgr, metrics.MustMakeRecorder(), sourcev1.SourceFinalizer)
cacheRecorder := cache.MustMakeMetrics()

eventRecorder, err := events.NewRecorder(ctrl.Log, eventsAddr, controllerName, events.WithManager(mgr))
var eventTLSConfig *tls.Config
if len(authorizedArtifactClients) > 0 || len(authorizedEventServers) > 0 {
source, err := workloadapi.NewX509Source(ctx)
if err != nil {
setupLog.Error(err, "unable to create SPIFFE X.509 source")
os.Exit(1)
}
defer func() {
if err := source.Close(); err != nil {
setupLog.Error(err, "unable to close SPIFFE X.509 source")
}
}()

if len(authorizedArtifactClients) > 0 {
artifactTLSConfig, err := fluxspiffe.BuildTLSConfig(source, authorizedArtifactClients...)
if err != nil {
setupLog.Error(err, "unable to configure SPIFFE mTLS for the artifact server")
os.Exit(1)
}
setupLog.Info("artifact server will use inter-controller mTLS via SPIFFE", "spiffeIDs", authorizedArtifactClients)
artifactOptions.TLSConfig = artifactTLSConfig
}

if len(authorizedEventServers) > 0 {
eventTLSConfig, err = fluxspiffe.BuildTLSConfig(source, authorizedEventServers...)
if err != nil {
setupLog.Error(err, "unable to configure SPIFFE mTLS for the event server")
os.Exit(1)
}
setupLog.Info("event server will use inter-controller mTLS via SPIFFE", "spiffeIDs", authorizedEventServers)
}
}

eventRecorderOpts := []events.RecorderOption{events.WithManager(mgr)}
if eventTLSConfig != nil {
eventRecorderOpts = append(eventRecorderOpts, events.WithTLSConfig(eventTLSConfig))
}
eventRecorder, err := events.NewRecorder(ctrl.Log, eventsAddr, controllerName, eventRecorderOpts...)
if err != nil {
setupLog.Error(err, "unable to create event recorder")
os.Exit(1)
Expand Down Expand Up @@ -225,8 +277,6 @@ func main() {
}
}

ctx := ctrl.SetupSignalHandler()

if err := (&controller.GitRepositoryReconciler{
Client: mgr.GetClient(),
Recorder: eventRecorder,
Expand Down
Loading