-
Notifications
You must be signed in to change notification settings - Fork 164
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
Add support to refer to values from ConfigMap in other namespaces #208
Conversation
…espaces Signed-off-by: Alexander Berger <alex-berger@gmx.ch>
b907418
to
a03bb2c
Compare
@alex-berger you can use Kyverno to generate a ConfigMap when a namespace is created. This allows you define the config data once (in the Kyverno ClusterPolicy) and it gets propagated to all your tenant namespaces. See Kyveron docs here: https://kyverno.io/docs/writing-policies/generate/ |
@stefanprodan I want to keep thing simple and Kyverno will not update the ConfigMap if it changes. I would rather prefer to add more flexiblity to the GitOps system (Flux) and not having to rely on Kyverno. |
From Kyverno docs:
|
@stefanprodan Kyverno is an implementation detail (orthogonal aspect) and I would rather prefer if we have first-class support for this in FluxCD, not having to rely on Kyverno or other policy engines. I will update the PR with the label based filter (opt-in) approach that I mentioned in the "Security Consideration" section and I will remove |
…s-in by setting the helm.fluxcd.io/share-with annotation Signed-off-by: Alexander Berger <alex-berger@gmx.ch>
I updated the PR, but I did not yet remove support for cross-namespace Examples: A kind: ConfigMap
apiVersion: v1
metadata:
name: public-cluster-info
namespace: kube-public
annotations:
helm.fluxcd.io/share-with: "*"
data: {} A kind: ConfigMap
apiVersion: v1
metadata:
name: public-cluster-info
namespace: kube-public
annotations:
helm.fluxcd.io/share-with: |-
- kube-system
- gloo-system
- my-namespace
data: {} If we want to add an additional layer of security, we could also feature-flag this, by adding a command line flag to the |
Signed-off-by: Alexander Berger <alex-berger@gmx.ch>
I need to have a think about the impact this would have in general, and on other Flux component / toolkit types that allow cross-namespace sources. One thing that must be changed if this were to be accepted is the domain of the annotation, it should be: |
…hare-with Signed-off-by: Alexander Berger <alex-berger@gmx.ch>
I assembled a table of all resource references fields in the current API, which might be helpful for
|
Wanted to let you know that I have not forgotten about this PR, we are planning on doing a new MINOR release today and once done I will have a proper look to take this into consideration for the next MINOR release. |
xref fluxcd/flux2#582 WRT security-model and multi-tenancy |
I feel the sharing annotation is unnecessary. The owner of the config/secret Namespace has full control of any RoleBinding that grants access for the configs to other Users/Groups from different Namespaces. We do need to ensure that helm-controller uses an impersonated client to fetch valuesFrom. |
if resource != nil { | ||
if resource.Namespace == namespace { | ||
// Rule 1. | ||
return nil | ||
} | ||
if resource.Annotations != nil { | ||
shareWithString := strings.TrimSpace(resource.Annotations[shareWithAnnotation]) | ||
if shareWithString == "*" { | ||
// Rule 2. | ||
return nil | ||
} | ||
if shareWithString != "" { | ||
var shareWith []string = make([]string, 0) | ||
if err := json.Unmarshal([]byte(shareWithString), &shareWith); err != nil { | ||
return fmt.Errorf( | ||
"%s '%s/%s' has invalid %s annotation value, expected string or array of string got '%s': %w", | ||
kind, resource.Namespace, resource.Name, shareWithAnnotation, shareWithString, err) | ||
} | ||
if slice.ContainsString(shareWith, namespace, nil) { | ||
// Rule 3. | ||
return nil | ||
} | ||
} | ||
} | ||
} | ||
// Rule 4. | ||
return fmt.Errorf( | ||
"%s '%s/%s' is not shared with namespace '%s' (change its '%s' annotation to enable sharing)", | ||
kind, resource.Namespace, resource.Name, namespace, shareWithAnnotation) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider exiting early to reduce nested logic.
No need to test a map[string]string
for nil before checking if it has a particular key, use ok
as demonstrated.
I took out checking for an empty string as that will fail unmarshalling which I felt simplified things, but that could be personal preference.
metav1.ResourceObj keys should use the Getters when available, ie. GetAnnotations()
if resource != nil { | |
if resource.Namespace == namespace { | |
// Rule 1. | |
return nil | |
} | |
if resource.Annotations != nil { | |
shareWithString := strings.TrimSpace(resource.Annotations[shareWithAnnotation]) | |
if shareWithString == "*" { | |
// Rule 2. | |
return nil | |
} | |
if shareWithString != "" { | |
var shareWith []string = make([]string, 0) | |
if err := json.Unmarshal([]byte(shareWithString), &shareWith); err != nil { | |
return fmt.Errorf( | |
"%s '%s/%s' has invalid %s annotation value, expected string or array of string got '%s': %w", | |
kind, resource.Namespace, resource.Name, shareWithAnnotation, shareWithString, err) | |
} | |
if slice.ContainsString(shareWith, namespace, nil) { | |
// Rule 3. | |
return nil | |
} | |
} | |
} | |
} | |
// Rule 4. | |
return fmt.Errorf( | |
"%s '%s/%s' is not shared with namespace '%s' (change its '%s' annotation to enable sharing)", | |
kind, resource.Namespace, resource.Name, namespace, shareWithAnnotation) | |
if resource == nil { | |
return fmt.Error("nil resource") | |
} | |
shareWithString, ok := resource.GetAnnotations()[shareWithAnnotation] | |
if !ok { | |
// Rule 4. | |
return fmt.Errorf( | |
"%s '%s/%s' is not shared with namespace '%s' (add an annotation, '%s', to enable sharing)", | |
kind, resource.Namespace, resource.Name, namespace, shareWithAnnotation) | |
} | |
shareWithString = strings.TrimSpace(shareWithString) | |
if resource.Namespace == namespace { | |
// Rule 1. | |
return nil | |
} | |
if shareWithString == "*" { | |
// Rule 2. | |
return nil | |
} | |
var shareWith []string | |
if err := json.Unmarshal([]byte(shareWithString), &shareWith); err != nil { | |
return fmt.Errorf( | |
"%s '%s/%s' has invalid %s annotation value, expected string or array of string got '%s': %w", | |
kind, resource.Namespace, resource.Name, shareWithAnnotation, shareWithString, err) | |
} | |
if !slice.ContainsString(shareWith, namespace, nil) { | |
// Rule 4. | |
return fmt.Errorf( | |
"%s '%s/%s' is not shared with namespace '%s' (change its '%s' annotation to enable sharing)", | |
kind, resource.Namespace, resource.Name, namespace, shareWithAnnotation) | |
} | |
// Rule 3. | |
return nil |
I hope this is helpful.
name: prod-tls-values | ||
namespace: other-namespace |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
accidental tab
name: prod-tls-values | |
namespace: other-namespace | |
name: prod-tls-values | |
namespace: other-namespace |
Hi, is this feature abandoned? For us is kinda killer point Thanks |
The general premise of Flux is that in a multi-tenant environment you will enable multi-tenancy lockdown mode. As @stefanprodan pointed out there are ways to multicast resources to tenant namespaces using 3rd-party components. I'm closing the PR now as it hasn't received any attention for 2 years so people don't keep waiting for it. /cc @hiddeco |
This PR adds support to refer to values from
ConfigMap
s andSecret
s in other namespaces to thevaluesFrom
field of theHelmRelease
resource.Motivation:
In our use case, the SRE team provisions Kubernetes cluster, providing some (global)
ConfigMap
s, readable by all tenants on the cluster. ThoseConfigMap
s contain cluster-specific information, which is needed by those tenants as input to theirHelmReleases
valuesFrom
fields. And hopefully, once 253 is merged, we can also refer to thoseConfigMap
s fromKustomization
resources.We cannot (pre)provision those
ConfigMap
s into each tenant's namespace upfront, as tenant (namespaces) can come and go and we want to maintain theConfigMap
s in a single place (e.g. in thekube-public
namespace).Security Consideration
We use Kyverno policies to enforce that all
HelmRelease
andKustomization
resources have theirserviceAccount
field set, to make sure each tenant can only accessConfigMap
s andSecret
s that he has permission to read. However, if you have any objections regarding cross-namespace references toSecrets
, then I will refactor this PR to only apply the proposed change toConfigMap
s.Alternatively, we can add a label or annotation which must be present on
ConfigMap
orSecret
resources to allow cross-namespace sharing (e.g.helm.fluxcd.io/shared: true
) and we only accept cross-namespace resources if that flag is set. With this approach the owner of the resource (ConfigMap
/Secret
) has full control whether he want's to allow cross-namespace acccess or not. If that label is not present or invalid, access will be denied.