Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow disabling cross-namespace references #305

Merged
merged 1 commit into from
Jan 28, 2022
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
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 && 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")
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 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.
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