Skip to content

Commit

Permalink
Disallow remote bases usage in Kustomize overlays
Browse files Browse the repository at this point in the history
Add an optional flag for disabling remote bases. While the `--no-remote-bases` is set to `false` by default, Flux users are encouraged to enable it on production system for security and performance reasons. Using Kustomize remote bases means that kustomize-controller must clone the remote repositories on every reconciliation instead of using the source-controller artifacts cache. Allowing remote bases on multi-tenant clusters, means platform admins have no control over which repositories make up the desired state.

Signed-off-by: Stefan Prodan <[email protected]>
  • Loading branch information
stefanprodan committed Apr 27, 2022
1 parent bcfd424 commit cafbe25
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 9 deletions.
3 changes: 2 additions & 1 deletion controllers/kustomization_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ type KustomizationReconciler struct {
ControllerName string
statusManager string
NoCrossNamespaceRefs bool
NoRemoteBases bool
DefaultServiceAccount string
KubeConfigOpts runtimeClient.KubeConfigOptions
}
Expand Down Expand Up @@ -655,7 +656,7 @@ func (r *KustomizationReconciler) build(ctx context.Context, workDir string, kus
return nil, fmt.Errorf("error decrypting env sources: %w", err)
}

m, err := secureBuildKustomization(workDir, dirPath)
m, err := secureBuildKustomization(workDir, dirPath, !r.NoRemoteBases)
if err != nil {
return nil, fmt.Errorf("kustomize build failed: %w", err)
}
Expand Down
20 changes: 15 additions & 5 deletions controllers/kustomization_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"strings"
"sync"

"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/krusty"
"sigs.k8s.io/kustomize/api/provider"
Expand Down Expand Up @@ -247,11 +248,20 @@ var kustomizeBuildMutex sync.Mutex
// - load files from outside the kustomization dir path
// (but not outside root)
// - disable plugins except for the builtin ones
func secureBuildKustomization(root, dirPath string) (_ resmap.ResMap, err error) {
// Create secure FS for root
fs, err := securefs.MakeFsOnDiskSecureBuild(root)
if err != nil {
return nil, err
func secureBuildKustomization(root, dirPath string, allowRemoteBases bool) (_ resmap.ResMap, err error) {
var fs filesys.FileSystem

// Create secure FS for root with or without remote base support
if allowRemoteBases {
fs, err = securefs.MakeFsOnDiskSecureBuild(root)
if err != nil {
return nil, err
}
} else {
fs, err = securefs.MakeFsOnDiskSecure(root)
if err != nil {
return nil, err
}
}

// Temporary workaround for concurrent map read and map write bug
Expand Down
13 changes: 10 additions & 3 deletions controllers/kustomization_generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,27 @@ func Test_secureBuildKustomization(t *testing.T) {
t.Run("remote build", func(t *testing.T) {
g := NewWithT(t)

_, err := secureBuildKustomization("testdata/remote", "testdata/remote")
_, err := secureBuildKustomization("testdata/remote", "testdata/remote", true)
g.Expect(err).ToNot(HaveOccurred())
})

t.Run("no remote build", func(t *testing.T) {
g := NewWithT(t)

_, err := secureBuildKustomization("testdata/remote", "testdata/remote", false)
g.Expect(err).To(HaveOccurred())
})
}

func Test_secureBuildKustomization_panic(t *testing.T) {
t.Run("build panic", func(t *testing.T) {
g := NewWithT(t)

_, err := secureBuildKustomization("testdata/panic", "testdata/panic")
_, err := secureBuildKustomization("testdata/panic", "testdata/panic", false)
g.Expect(err).To(HaveOccurred())
g.Expect(err.Error()).To(ContainSubstring("recovered from kustomize build panic"))
// Run again to ensure the lock is released
_, err = secureBuildKustomization("testdata/panic", "testdata/panic")
_, err = secureBuildKustomization("testdata/panic", "testdata/panic", false)
g.Expect(err).To(HaveOccurred())
})
}
4 changes: 4 additions & 0 deletions docs/spec/v1beta2/kustomization.md
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,10 @@ spec:
path: "./deploy/production"
```
For security and performance reasons, it is advised to disallow the usage of
[remote bases](https://github.com/kubernetes-sigs/kustomize/blob/a7f4db7fb41e17b2c826a524f545e6174b4dc6ac/examples/remoteBuild.md)
in Kustomize overlays. To enforce this setting, platform admins can use the `--no-remote-bases=true` flag.

## Source reference

The Kustomization `spec.sourceRef` is a reference to an object managed by
Expand Down
4 changes: 4 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ func main() {
rateLimiterOptions helper.RateLimiterOptions
aclOptions acl.Options
watchAllNamespaces bool
noRemoteBases bool
httpRetry int
defaultServiceAccount string
)
Expand All @@ -88,6 +89,8 @@ func main() {
flag.DurationVar(&requeueDependency, "requeue-dependency", 30*time.Second, "The interval at which failing dependencies are reevaluated.")
flag.BoolVar(&watchAllNamespaces, "watch-all-namespaces", true,
"Watch for custom resources in all namespaces, if set to false it will only watch the runtime namespace.")
flag.BoolVar(&noRemoteBases, "no-remote-bases", false,
"Disallow remote bases usage in Kustomize overlays. When this flag is enabled, all resources must refer to local files included in the source artifact.")
flag.IntVar(&httpRetry, "http-retry", 9, "The maximum number of retries when failing to fetch artifacts over HTTP.")
flag.StringVar(&defaultServiceAccount, "default-service-account", "", "Default service account used for impersonation.")
clientOptions.BindFlags(flag.CommandLine)
Expand Down Expand Up @@ -146,6 +149,7 @@ func main() {
EventRecorder: eventRecorder,
MetricsRecorder: metricsRecorder,
NoCrossNamespaceRefs: aclOptions.NoCrossNamespaceRefs,
NoRemoteBases: noRemoteBases,
KubeConfigOpts: kubeConfigOpts,
StatusPoller: polling.NewStatusPoller(mgr.GetClient(), mgr.GetRESTMapper(), polling.Options{
CustomStatusReaders: []engine.StatusReader{jobStatusReader},
Expand Down

0 comments on commit cafbe25

Please sign in to comment.