Merge pull request #305 from SomtochiAma/no-cross-ns-ref

Allow disabling cross-namespace references
This commit is contained in:
Stefan Prodan 2022-01-28 09:49:16 +02:00 committed by GitHub
commit 6aaf01a6ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 84 additions and 13 deletions

View File

@ -29,7 +29,6 @@ import (
"time"
"github.com/Masterminds/sprig/v3"
gogit "github.com/go-git/go-git/v5"
libgit2 "github.com/libgit2/git2go/v31"
@ -55,7 +54,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/source"
imagev1_reflect "github.com/fluxcd/image-reflector-controller/api/v1beta1"
apiacl "github.com/fluxcd/pkg/apis/acl"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/runtime/acl"
"github.com/fluxcd/pkg/runtime/events"
"github.com/fluxcd/pkg/runtime/logger"
"github.com/fluxcd/pkg/runtime/metrics"
@ -91,6 +92,7 @@ type ImageUpdateAutomationReconciler struct {
EventRecorder kuberecorder.EventRecorder
ExternalEventRecorder *events.Recorder
MetricsRecorder *metrics.Recorder
NoCrossNamespaceRef bool
}
type ImageUpdateAutomationReconcilerOptions struct {
@ -179,6 +181,19 @@ func (r *ImageUpdateAutomationReconciler) Reconcile(ctx context.Context, req ctr
}
debuglog.Info("fetching git repository", "gitrepository", originName)
if r.NoCrossNamespaceRef && gitRepoNamespace != auto.GetNamespace() {
err := acl.AccessDeniedError(fmt.Sprintf("can't access '%s/%s', cross-namespace references have been blocked",
auto.Spec.SourceRef.Kind, originName))
log.Error(err, "access denied to cross-namespaced resource")
imagev1.SetImageUpdateAutomationReadiness(&auto, metav1.ConditionFalse, apiacl.AccessDeniedReason,
err.Error())
if err := r.patchStatus(ctx, req, auto.Status); err != nil {
return ctrl.Result{Requeue: true}, err
}
r.event(ctx, auto, events.EventSeverityError, err.Error())
return ctrl.Result{}, nil
}
if err := r.Get(ctx, originName, &origin); err != nil {
if client.IgnoreNotFound(err) == nil {
imagev1.SetImageUpdateAutomationReadiness(&auto, metav1.ConditionFalse, imagev1.GitNotAvailableReason, "referenced git repository is missing")

View File

@ -30,6 +30,7 @@ import (
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/ProtonMail/go-crypto/openpgp/armor"
"github.com/fluxcd/pkg/apis/acl"
"github.com/go-git/go-billy/v5/memfs"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config"
@ -268,6 +269,7 @@ Images:
})
AfterEach(func() {
imageAutoReconciler.NoCrossNamespaceRef = false
Expect(k8sClient.Delete(context.Background(), namespace)).To(Succeed())
})
@ -290,8 +292,9 @@ Images:
Context("ref cross-ns GitRepository", func() {
var (
localRepo *git.Repository
commitMessage string
localRepo *git.Repository
commitMessage string
updateBySetters *imagev1.ImageUpdateAutomation
)
const (
@ -410,7 +413,7 @@ Images:
Namespace: namespace.Name,
Name: "update-test",
}
updateBySetters := &imagev1.ImageUpdateAutomation{
updateBySetters = &imagev1.ImageUpdateAutomation{
ObjectMeta: metav1.ObjectMeta{
Name: updateKey.Name,
Namespace: updateKey.Namespace,
@ -465,6 +468,28 @@ Images:
Expect(commit.Author.Name).To(Equal(authorName))
Expect(commit.Author.Email).To(Equal(authorEmail))
})
It("fails to reconcile if cross-namespace flag is set", func() {
imageAutoReconciler.NoCrossNamespaceRef = true
// trigger reconcile
var updatePatch imagev1.ImageUpdateAutomation
Expect(k8sClient.Get(context.TODO(), client.ObjectKeyFromObject(updateBySetters), &updatePatch)).To(Succeed())
updatePatch.Spec.Interval = metav1.Duration{Duration: 5 * time.Minute}
Expect(k8sClient.Patch(context.Background(), &updatePatch, client.Merge)).To(Succeed())
resultAuto := &imagev1.ImageUpdateAutomation{}
var readyCondition *metav1.Condition
Eventually(func() bool {
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(updateBySetters), resultAuto)
readyCondition = apimeta.FindStatusCondition(resultAuto.Status.Conditions, meta.ReadyCondition)
return apimeta.IsStatusConditionFalse(resultAuto.Status.Conditions, meta.ReadyCondition)
}, timeout, time.Second).Should(BeTrue())
Expect(readyCondition).ToNot(BeNil())
Expect(readyCondition.Reason).To(Equal(acl.AccessDeniedReason))
})
})
Context("update path", func() {

View File

@ -32,7 +32,7 @@ type ImageUpdateAutomationSpec struct {
// SourceRef refers to the resource giving access details
// to a git repository.
// +required
SourceRef SourceReference `json:"sourceRef"`
SourceRef CrossNamespaceSourceReference `json:"sourceRef"`
// GitSpec contains all the git-specific definitions. This is
// technically optional, but in practice mandatory until there are
// other kinds of source allowed.
@ -62,25 +62,51 @@ repository to be updated. The `kind` field in the reference currently only suppo
`GitRepository`, which is the default.
```go
// SourceReference contains enough information to let you locate the
// typed, referenced source object.
type SourceReference struct {
// API version of the referent
// CrossNamespaceSourceReference contains enough information to let you locate the
// typed Kubernetes resource object at cluster level.
type CrossNamespaceSourceReference struct {
// API version of the referent.
// +optional
APIVersion string `json:"apiVersion,omitempty"`
// Kind of the referent
// Kind of the referent.
// +kubebuilder:validation:Enum=GitRepository
// +kubebuilder:default=GitRepository
// +required
Kind string `json:"kind"`
// Name of the referent
// Name of the referent.
// +required
Name string `json:"name"`
// Namespace of the referent, defaults to the namespace of the Kubernetes resource object that contains the reference.
// +optional
Namespace string `json:"namespace,omitempty"`
}
```
### Cross-namespace references
A ImageUpdateAutomation can refer to a GitRepository from a different namespace with
`spec.sourceRef.namespace` e.g.:
```yaml
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageUpdateAutomation
metadata:
name: webapp
namespace: apps
spec:
interval: 5m
sourceRef:
kind: GitRepository # the only valid value, but good practice to be explicit here
name: apps
namespace: flux-system
```
On multi-tenant clusters, platform admins can disable cross-namespace references with the
`--no-cross-namespace-refs=true` flag.
To be able to commit changes back, the referenced `GitRepository` object must refer to credentials
with write access; e.g., if using a GitHub deploy key, "Allow write access" should be checked when
creating it. Only the `url`, `ref`, and `secretRef` fields of the `GitRepository` are used.

2
go.mod
View File

@ -13,7 +13,7 @@ require (
github.com/fluxcd/image-reflector-controller/api v0.15.0
github.com/fluxcd/pkg/apis/meta v0.10.2
github.com/fluxcd/pkg/gittestserver v0.5.0
github.com/fluxcd/pkg/runtime v0.12.3
github.com/fluxcd/pkg/runtime v0.12.4
github.com/fluxcd/pkg/ssh v0.2.0
// If you bump this, change SOURCE_VER in the Makefile to match
github.com/fluxcd/source-controller v0.21.0

3
go.sum
View File

@ -366,8 +366,9 @@ github.com/fluxcd/pkg/gitutil v0.1.0 h1:VO3kJY/CKOCO4ysDNqfdpTg04icAKBOSb3lbR5uE
github.com/fluxcd/pkg/gitutil v0.1.0/go.mod h1:Ybz50Ck5gkcnvF0TagaMwtlRy3X3wXuiri1HVsK5id4=
github.com/fluxcd/pkg/helmtestserver v0.4.0/go.mod h1:JOI9f3oXUFIWmMKWMBan7FjglAU+fRTO/sPPV/Kj3gQ=
github.com/fluxcd/pkg/lockedfile v0.1.0/go.mod h1:EJLan8t9MiOcgTs8+puDjbE6I/KAfHbdvIy9VUgIjm8=
github.com/fluxcd/pkg/runtime v0.12.3 h1:h21AZ3YG5MAP7DxFF9hfKrP+vFzys2L7CkUbPFjbP/0=
github.com/fluxcd/pkg/runtime v0.12.3/go.mod h1:imJ2xYy/d4PbSinX2IefmZk+iS2c1P5fY0js8mCE4SM=
github.com/fluxcd/pkg/runtime v0.12.4 h1:gA27RG/+adN2/7Qe03PB46Iwmye/MusPCpuS4zQ2fW0=
github.com/fluxcd/pkg/runtime v0.12.4/go.mod h1:gspNvhAqodZgSmK1ZhMtvARBf/NGAlxmaZaIOHkJYsc=
github.com/fluxcd/pkg/ssh v0.2.0 h1:e9V+HReOL7czm7edVzYS1e+CnFKz1/kHiUNfLRpBdH8=
github.com/fluxcd/pkg/ssh v0.2.0/go.mod h1:EpQC7Ztdlbi8S/dlYXqVDZtHtLpN3FNl3N6zWujVzbA=
github.com/fluxcd/pkg/testserver v0.1.0/go.mod h1:fvt8BHhXw6c1+CLw1QFZxcQprlcXzsrL4rzXaiGM+Iw=

View File

@ -29,6 +29,7 @@ import (
ctrlmetrics "sigs.k8s.io/controller-runtime/pkg/metrics"
imagev1_reflect "github.com/fluxcd/image-reflector-controller/api/v1beta1"
"github.com/fluxcd/pkg/runtime/acl"
"github.com/fluxcd/pkg/runtime/client"
"github.com/fluxcd/pkg/runtime/events"
"github.com/fluxcd/pkg/runtime/leaderelection"
@ -64,6 +65,7 @@ func main() {
eventsAddr string
healthAddr string
clientOptions client.Options
aclOptions acl.Options
logOptions logger.Options
leaderElectionOptions leaderelection.Options
watchAllNamespaces bool
@ -79,6 +81,7 @@ func main() {
clientOptions.BindFlags(flag.CommandLine)
logOptions.BindFlags(flag.CommandLine)
leaderElectionOptions.BindFlags(flag.CommandLine)
aclOptions.BindFlags(flag.CommandLine)
flag.Parse()
log := logger.NewLogger(logOptions)
@ -130,6 +133,7 @@ func main() {
EventRecorder: mgr.GetEventRecorderFor(controllerName),
ExternalEventRecorder: eventRecorder,
MetricsRecorder: metricsRecorder,
NoCrossNamespaceRef: aclOptions.NoCrossNamespaceRefs,
}).SetupWithManager(mgr, controllers.ImageUpdateAutomationReconcilerOptions{
MaxConcurrentReconciles: concurrent,
}); err != nil {