Skip to content

Commit

Permalink
Add flag to disable cross-namespace referencing
Browse files Browse the repository at this point in the history
Signed-off-by: Somtochi Onyekwere <[email protected]>
  • Loading branch information
somtochiama committed Jan 27, 2022
1 parent 895bb22 commit 3c195fd
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 13 deletions.
17 changes: 16 additions & 1 deletion controllers/imageupdateautomation_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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"
Expand Down Expand Up @@ -91,6 +92,7 @@ type ImageUpdateAutomationReconciler struct {
EventRecorder kuberecorder.EventRecorder
ExternalEventRecorder *events.Recorder
MetricsRecorder *metrics.Recorder
NoCrossNamespaceRef bool
}

type ImageUpdateAutomationReconcilerOptions struct {
Expand Down Expand Up @@ -179,6 +181,19 @@ func (r *ImageUpdateAutomationReconciler) Reconcile(ctx context.Context, req ctr
}
debuglog.Info("fetching git repository", "gitrepository", originName)

if r.NoCrossNamespaceRef && auto.Spec.SourceRef.Namespace != 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")
Expand Down
31 changes: 28 additions & 3 deletions controllers/update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -268,6 +269,7 @@ Images:
})

AfterEach(func() {
imageAutoReconciler.NoCrossNamespaceRef = false
Expect(k8sClient.Delete(context.Background(), namespace)).To(Succeed())
})

Expand All @@ -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 (
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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() {
Expand Down
40 changes: 33 additions & 7 deletions docs/spec/v1beta1/imageupdateautomations.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 ImageUpdateAutomationS 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: imageupdateautomation-sample
namespace: flux-system
spec:
interval: 5m
sourceRef:
kind: GitRepository # the only valid value, but good practice to be explicit here
name: sample-repo
namespace: default
```
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.
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
4 changes: 4 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -64,6 +65,7 @@ func main() {
eventsAddr string
healthAddr string
clientOptions client.Options
aclOptions acl.Options
logOptions logger.Options
leaderElectionOptions leaderelection.Options
watchAllNamespaces bool
Expand All @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit 3c195fd

Please sign in to comment.