Skip to content

Commit 0701cb8

Browse files
authored
swap caching for non-caching reads of Secrets (#72)
In order to allow the ClusterRole associated with the ServiceAccount of a controller started in Cluster Mode to require Read/List/Watch permissions on Secrets in ALL Kubernetes Namespaces, we need to change the way that Secret values are read in the resource reconciler. When a "normal" client-go Kubernetes client is created and a single watch namespace is *not* provided, then whenever that client is used to read object information, a new SharedInformer cache is created for that Kind of resource across *all* Namespaces. This SharedInformer cache sets up listers and watchers on all resources of that Kind of resource across all Namespaces and thus the ClusterRole associated with the ServiceAccount of the controller needs permissions to get, list and watch those resources across all Namespaces. For Secret resources, this permission to list/watch Secrets across all Namespaces is too broad for many security-conscious organizations. For these organizations, they wish to restrict the ACK controller to only watch Secret resources that are in specific Namespaces that the ACK custom resources are created in. In order to fulfill this security requirement, we needed to make a relatively small change to the resource reconciler's SecretValueFromReference method to use the non-caching apiReader part of the Kubernetes client. The other changes included in this patch revolve around documentation and renaming the environment variable used to determine the Namespace that the CARM ConfigMap resides in. Previously, the environment variable controlling the Namespace the `ack-role-account-map` ConfigMap was in was called `K8S_NAMESPACE`. I thought this name was overly broad and confusing, considering we have a `--watch-namespace` CLI flag for determining if the ACK controller is started in "Cluster Mode" or "Namespace Mode", and the generic `K8S_NAMESPACE` environment variable name was being confused with this entirely unrelated CLI flag. I have thus renamed `K8S_NAMESPACE` to `ACK_SYSTEM_NAMESPACE` to more accurately reflect what the variable configures. I have left support for falling back to the old, now deprecated `K8S_NAMESPACE` name, however. Finally, I removed the hard-coded string `ack-system` comparison in the CARM Account cache and now have it checking the ConfigMap's Namespace against the value of the `ACK_SYSTEM_NAMESPACE` environment variable. Note that I have run RDS controller local kind tests against this version of the ACK runtime to ensure there are no nasty surprises and all e2e tests are passing properly. Issue: aws-controllers-k8s/community#1173 Signed-off-by: Jay Pipes <[email protected]> By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 65b9179 commit 0701cb8

File tree

4 files changed

+33
-17
lines changed

4 files changed

+33
-17
lines changed

pkg/runtime/cache/account.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func resourceMatchACKRoleAccountsConfigMap(raw interface{}) bool {
5757
func (c *AccountCache) Run(clientSet kubernetes.Interface, stopCh <-chan struct{}) {
5858
informer := informersv1.NewConfigMapInformer(
5959
clientSet,
60-
currentNamespace,
60+
ackSystemNamespace,
6161
informerResyncPeriod,
6262
k8scache.Indexers{},
6363
)

pkg/runtime/cache/cache.go

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,33 +14,48 @@
1414
package cache
1515

1616
import (
17-
"os"
1817
"time"
1918

2019
"github.com/go-logr/logr"
20+
"github.com/jaypipes/envutil"
2121
kubernetes "k8s.io/client-go/kubernetes"
2222
)
2323

2424
const (
25-
// defaultNamespace is the default namespace to use if the environment
26-
// variable NAMESPACE is not found. The NAMESPACE variable is injected
27-
// using the kubernetes downward api.
28-
defaultNamespace = "ack-system"
25+
// envVarACKSystemNamespace is the string key for the environment variable
26+
// storing the Kubernetes Namespace we use for ConfigMaps and other ACK
27+
// system configuration needs.
28+
envVarACKSystemNamespace = "ACK_SYSTEM_NAMESPACE"
29+
30+
// envVarDeprecatedK8sNamespace is the string key for the old, deprecated
31+
// environment variable storing the Kubernetes Namespace we use for
32+
// ConfigMaps and other ACK system configuration needs.
33+
envVarDeprecatedK8sNamespace = "K8S_NAMESPACE"
34+
35+
// defaultACKSystemNamespace is the namespace we look up the CARM account
36+
// map ConfigMap in if the environment variable ACK_SYSTEM_NAMESPACE is not
37+
// found.
38+
defaultACKSystemNamespace = "ack-system"
2939

3040
// informerDefaultResyncPeriod is the period at which ShouldResync
3141
// is considered.
42+
// NOTE(jaypipes): setting this to zero means we are telling the client-go
43+
// caching system not to set up resyncs with an authoritative state source
44+
// (i.e. a Kubernetes API server) on a periodic basis.
3245
informerResyncPeriod = 0 * time.Second
3346
)
3447

35-
// currentNamespace is the namespace in which the current service
36-
// controller Pod is running
37-
var currentNamespace string
48+
// ackSystemNamespace is the namespace in which we look up ACK system
49+
// configuration (ConfigMaps, etc)
50+
var ackSystemNamespace string
3851

3952
func init() {
40-
currentNamespace = os.Getenv("K8S_NAMESPACE")
41-
if currentNamespace == "" {
42-
currentNamespace = defaultNamespace
43-
}
53+
ackSystemNamespace = envutil.WithDefault(
54+
envVarACKSystemNamespace, envutil.WithDefault(
55+
envVarDeprecatedK8sNamespace,
56+
defaultACKSystemNamespace,
57+
),
58+
)
4459
}
4560

4661
// Caches is used to interact with the different caches

pkg/runtime/cache/namespace.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,13 @@ func NewNamespaceCache(log logr.Logger) *NamespaceCache {
7676
}
7777
}
7878

79-
// isIgnoredNamespace returns true if an object is of type corev1.Namespace
80-
// and its metadata name is one of 'ack-system', 'kube-system' or 'kube-public'
79+
// isIgnoredNamespace returns true if an object is of type corev1.Namespace and
80+
// its metadata name is the ACK system namespace, 'kube-system' or
81+
// 'kube-public'
8182
func isIgnoredNamespace(raw interface{}) bool {
8283
object, ok := raw.(*corev1.Namespace)
8384
return ok &&
84-
(object.ObjectMeta.Name == "ack-system" ||
85+
(object.ObjectMeta.Name == ackSystemNamespace ||
8586
object.ObjectMeta.Name == "kube-system" ||
8687
object.ObjectMeta.Name == "kube-public")
8788
}

pkg/runtime/reconciler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ func (r *reconciler) SecretValueFromReference(
111111
Name: ref.Name,
112112
}
113113
var secret corev1.Secret
114-
if err := r.kc.Get(ctx, nsn, &secret); err != nil {
114+
if err := r.apiReader.Get(ctx, nsn, &secret); err != nil {
115115
return "", ackerr.SecretNotFound
116116
}
117117

0 commit comments

Comments
 (0)