-
Notifications
You must be signed in to change notification settings - Fork 100
NE-1476: Added network policies for DNS #458
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| # We control the namespace, deny anything we do not explicitly allow | ||
| apiVersion: networking.k8s.io/v1 | ||
| kind: NetworkPolicy | ||
| metadata: | ||
| name: dns-operator-deny-all | ||
| namespace: openshift-dns-operator | ||
| annotations: | ||
| include.release.openshift.io/self-managed-high-availability: "true" | ||
| include.release.openshift.io/single-node-developer: "true" | ||
| spec: | ||
| podSelector: {} | ||
| policyTypes: | ||
| - Ingress | ||
| - Egress | ||
| --- | ||
| ### Allow the operators to talk to the apiserver and dns | ||
| ### Allow access to the metrics ports on the operators | ||
| apiVersion: networking.k8s.io/v1 | ||
| kind: NetworkPolicy | ||
| metadata: | ||
| name: dns-operator-allow | ||
| namespace: openshift-dns-operator | ||
| annotations: | ||
| include.release.openshift.io/self-managed-high-availability: "true" | ||
| include.release.openshift.io/single-node-developer: "true" | ||
| spec: | ||
| podSelector: | ||
| matchLabels: | ||
| name: dns-operator | ||
| policyTypes: | ||
| - Egress | ||
| - Ingress | ||
| egress: | ||
| - ports: | ||
| - protocol: TCP | ||
| port: 6443 | ||
| - protocol: TCP | ||
| port: 53 | ||
| - protocol: UDP | ||
| port: 53 | ||
| - to: | ||
| - namespaceSelector: | ||
| matchLabels: | ||
| kubernetes.io/metadata.name: openshift-apiserver | ||
| - podSelector: | ||
| matchLabels: | ||
| apiserver: "true" | ||
| ingress: | ||
| - from: | ||
| - namespaceSelector: | ||
| matchLabels: | ||
| kubernetes.io/metadata.name: openshift-monitoring | ||
| ports: | ||
| - protocol: TCP | ||
| port: 9393 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,3 +27,15 @@ rules: | |
| - services | ||
| verbs: | ||
| - "*" | ||
|
|
||
| - apiGroups: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we really need this to be added to the Role? I can see the following ClusterRoleBinding on the cluster: Which means the Pod from the operator (who will be creating the Network Policies) already has its permissions. I am not sure why the dns-operator role exists and why it would need to be able to add Network Policies as well |
||
| - networking.k8s.io | ||
| resources: | ||
| - networkpolicies | ||
| verbs: | ||
| - get | ||
| - list | ||
| - create | ||
| - update | ||
| - watch | ||
| - delete | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| # DNS Pods | ||
| # Egress to api server and upstream dns (can be wildcarded, so allow any egress) | ||
| # Ingress to dns on port 5353 (TCP and UDP) and to metrics (9154) | ||
| apiVersion: networking.k8s.io/v1 | ||
| kind: NetworkPolicy | ||
| # name, namespace,labels and annotations are set at runtime | ||
| spec: | ||
| podSelector: | ||
| # matchLabels are set at runtime | ||
| matchLabels: {} | ||
| ingress: | ||
| # Everyone needs to be able to access the dns ports | ||
| - ports: | ||
| - protocol: UDP | ||
| port: 5353 | ||
| - protocol: TCP | ||
| port: 5353 | ||
| # Allow monitoring to hit the metrics port | ||
| - ports: | ||
| - protocol: TCP | ||
| port: 9154 | ||
| from: | ||
| - namespaceSelector: | ||
| matchLabels: | ||
| kubernetes.io/metadata.name: openshift-monitoring | ||
| # Allow the dns operator namespaces to hit the healthcheck ports | ||
| - ports: | ||
| - protocol: TCP | ||
| port: 8080 | ||
| - protocol: TCP | ||
| port: 8181 | ||
| from: | ||
| - namespaceSelector: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this will fail, the namespaceSelector.matchLabels is an AND operator. You are saying here you want traffic just from pods that are on a namespace that contains the labels metadata.name=openshift-dns-operator AND metadata.name=openshift-dns Instead, you could do something like: Tho, I am wondering why workloads running on openshift-dns would need to be able to reach metrics from other workloads on the same namespace. Would it make sense to limit just "from: openshift-dns-operator"?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah btw this is also conflicting, matchLabels is a map[string]string so you are setting the same key twice. Probably worth removing "openshift-dns" from here and leaving just openshift-dns-operator |
||
| matchLabels: | ||
| kubernetes.io/metadata.name: openshift-dns-operator | ||
| kubernetes.io/metadata.name: openshift-dns | ||
| egress: | ||
| - to: | ||
| - ipBlock: | ||
| cidr: 0.0.0.0/0 | ||
| policyTypes: | ||
| - Ingress | ||
| - Egress | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| # Default deny all policy for all pods in the namespace | ||
| apiVersion: networking.k8s.io/v1 | ||
| kind: NetworkPolicy | ||
| metadata: | ||
| name: openshift-dns-deny-all | ||
| namespace: openshift-dns | ||
| spec: | ||
| podSelector: {} | ||
| policyTypes: | ||
| - Ingress | ||
| - Egress |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| package controller | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
|
|
||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| operatorv1 "github.com/openshift/api/operator/v1" | ||
| "github.com/openshift/cluster-dns-operator/pkg/manifests" | ||
|
|
||
| "github.com/sirupsen/logrus" | ||
|
|
||
| networkingv1 "k8s.io/api/networking/v1" | ||
| "k8s.io/apimachinery/pkg/api/errors" | ||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
| ) | ||
|
|
||
| // ensureDNSNetworkPolicy ensures that a NetworkPolicy exists for the given DNS. | ||
| func (r *reconciler) ensureDNSNetworkPolicy(dns *operatorv1.DNS) (bool, *networkingv1.NetworkPolicy, error) { | ||
| haveNP, current, err := r.currentDNSNetworkPolicy(dns) | ||
| if err != nil { | ||
| return false, nil, err | ||
| } | ||
|
|
||
| desired := desiredDNSNetworkPolicy(dns) | ||
|
|
||
| switch { | ||
| case !haveNP: | ||
| if err := r.client.Create(context.TODO(), desired); err != nil { | ||
| return false, nil, fmt.Errorf("failed to create dns networkpolicy: %v", err) | ||
| } | ||
| logrus.Infof("created dns networkpolicy: %s/%s", desired.Namespace, desired.Name) | ||
| return r.currentDNSNetworkPolicy(dns) | ||
| case haveNP: | ||
| if updated, err := r.updateDNSNetworkPolicy(current, desired); err != nil { | ||
| return true, current, err | ||
| } else if updated { | ||
| return r.currentDNSNetworkPolicy(dns) | ||
| } | ||
| } | ||
| return true, current, nil | ||
| } | ||
|
|
||
| func (r *reconciler) currentDNSNetworkPolicy(dns *operatorv1.DNS) (bool, *networkingv1.NetworkPolicy, error) { | ||
| current := &networkingv1.NetworkPolicy{} | ||
| if err := r.client.Get(context.TODO(), DNSNetworkPolicyName(dns), current); err != nil { | ||
| if errors.IsNotFound(err) { | ||
| return false, nil, nil | ||
| } | ||
| return false, nil, err | ||
| } | ||
| return true, current, nil | ||
| } | ||
|
|
||
| func desiredDNSNetworkPolicy(dns *operatorv1.DNS) *networkingv1.NetworkPolicy { | ||
| np := manifests.DNSNetworkPolicy() | ||
|
|
||
| name := DNSNetworkPolicyName(dns) | ||
| np.Namespace = name.Namespace | ||
| np.Name = name.Name | ||
| np.SetOwnerReferences([]metav1.OwnerReference{dnsOwnerRef(dns)}) | ||
|
|
||
| if np.Labels == nil { | ||
| np.Labels = map[string]string{} | ||
| } | ||
| np.Labels[manifests.OwningDNSLabel] = DNSDaemonSetLabel(dns) | ||
|
|
||
| // Ensure pod selector matches the DNS daemonset pods for this instance. | ||
| if sel := DNSDaemonSetPodSelector(dns); sel != nil { | ||
| np.Spec.PodSelector = *sel | ||
| } | ||
|
|
||
| return np | ||
| } | ||
|
|
||
| func (r *reconciler) updateDNSNetworkPolicy(current, desired *networkingv1.NetworkPolicy) (bool, error) { | ||
| changed, updated := networkPolicyChanged(current, desired) | ||
| if !changed { | ||
| return false, nil | ||
| } | ||
|
|
||
| // Diff before updating because the client may mutate the object. | ||
| diff := cmp.Diff(current, updated, cmpopts.EquateEmpty()) | ||
| if err := r.client.Update(context.TODO(), updated); err != nil { | ||
| return false, fmt.Errorf("failed to update dns networkpolicy %s/%s: %v", updated.Namespace, updated.Name, err) | ||
| } | ||
| logrus.Infof("updated dns networkpolicy %s/%s: %v", updated.Namespace, updated.Name, diff) | ||
| return true, nil | ||
| } | ||
|
|
||
| func networkPolicyChanged(current, expected *networkingv1.NetworkPolicy) (bool, *networkingv1.NetworkPolicy) { | ||
| // Ignore fields that the API, other controllers, or users may modify. | ||
| npCmpOpts := []cmp.Option{ | ||
| cmpopts.EquateEmpty(), | ||
| } | ||
|
|
||
| currentLabels := current.Labels | ||
| if currentLabels == nil { | ||
| currentLabels = map[string]string{} | ||
| } | ||
| expectedLabels := expected.Labels | ||
| if expectedLabels == nil { | ||
| expectedLabels = map[string]string{} | ||
| } | ||
|
|
||
| if cmp.Equal(current.Spec, expected.Spec, npCmpOpts...) && cmp.Equal(currentLabels, expectedLabels, npCmpOpts...) { | ||
| return false, nil | ||
| } | ||
|
|
||
| updated := current.DeepCopy() | ||
| updated.Spec = expected.Spec | ||
| updated.Labels = map[string]string{} | ||
| for k, v := range expectedLabels { | ||
| updated.Labels[k] = v | ||
| } | ||
|
|
||
| return true, updated | ||
| } |
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.
@rfredette this rule seems a bit weird, so let me echo what you tried to do here:
If so, what you need are two egress rules (right now what your rule is saying: allow any egress to port 6443, 53 TCP and UDP, and also allow any egress to any pod with label apiserver=true on namespace openshift-apiserver
I guess the network policy you were expecting is something like this: