Bug #117075 Support for networkpolicy
Submitted: 30 Dec 2024 13:23 Modified: 30 Dec 2024 14:02
Reporter: Martin Fleurke (OCA) Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Operator Severity:S4 (Feature request)
Version:9.1.0-2.2.2 OS:Any
Assigned to: CPU Architecture:Any

[30 Dec 2024 13:23] Martin Fleurke
Description:
The MySQL Operator doesn't come with any support of k8s NetworkPolicy.
If your cluster has a default network policy that denies all traffic, the operator will not be able to connect to the nodes, nor the backup job pods or router.

If I create my own network policies, and deploy them using helm, with mysql-innodbcluster as subchart, i can make it work, but if I uninstall the helm chart, it is likely that the network policies are removed before the operatore removes the cluster, resulting in that the operator cannot reach the cluster any more that it needs to remove. This can only be properly solved by adding the operator as finalizer of the network policies.

How to repeat:
deploy a default network policy like:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  annotations:
    k8s.portavita.net/hint: deny-all
  name: default-network-policy
spec:
  egress:
  - ports:
    - port: 53
      protocol: UDP
    - port: 53
      protocol: TCP
    to:
    - namespaceSelector:
        matchLabels:
          name: kube-system
      podSelector:
        matchLabels:
          k8s-app: kube-dns
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

then deploy a mysql-innodbcluster and operator

Suggested fix:
optionally include network policies in the helm charts.

The network policies need the operator as finalizer, so they are deleted after the cluster has been deleted.

the backup jobs will need a default label to be able to match them

netpol for the operator:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: {{ .Release.Name }}
  labels:
    release: {{ .Release.Name }}
spec:
  policyTypes:
    - Egress
  egress:
    - to:
      {{- range .Values.netpol.ApiServerIps }}
      - ipBlock:
          cidr: {{ . }}/32
      {{- end }}
      - namespaceSelector:
          matchLabels:
            name: kube-system
        podSelector:
          matchLabels:
            component: kube-apiserver
            tier: control-plane
      ports:
        - port: {{ .Values.netpol.apiPort }}
    - to:
      - namespaceSelector: { }
        podSelector:
          matchLabels:
            app.kubernetes.io/component: database
            app.kubernetes.io/created-by: mysql-operator
            app.kubernetes.io/managed-by: mysql-operator
      ports:
        - port: 3306 #From MySQL Shell to the MySQL server (classic MySQL protocol).
          protocol: TCP
        - port: 33060 #From MySQL Shell to the MySQL server (X Protocol)
          protocol: TCP
  podSelector:
    matchLabels:
      name: mysql-operator

netpol cluster:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: {{ .Release.Name }}-cluster
  labels:
    release: {{ .Release.Name }}
spec:
  policyTypes:
    - Egress
    - Ingress
  egress:
    - to: #apiserver
      {{- range .Values.netpol.ApiServerIps }}
      - ipBlock:
          cidr: {{ . }}/32
      {{- end }}
      - namespaceSelector:
          matchLabels:
            name: kube-system
        podSelector:
          matchLabels:
            component: kube-apiserver
            tier: control-plane
      ports:
        - port: {{ .Values.netpol.apiPort }}
    - to: #peers
      - podSelector:
          matchLabels:
            app.kubernetes.io/component: database
            app.kubernetes.io/created-by: mysql-operator
            app.kubernetes.io/managed-by: mysql-operator
            app.kubernetes.io/name: mysql-innodbcluster-mysql-server
            app.kubernetes.io/instance: mysql-innodbcluster-{{ .Release.Name }}-mysql-server
      ports:
        - port: 3306
          protocol: TCP
  ingress:
    - from: #operator
      - namespaceSelector:
          matchLabels:
            name: kube-system
        podSelector:
          matchLabels:
            name: mysql-operator
      ports:
        - port: 3306
          protocol: TCP
        - port: 33060
          protocol: TCP
    - from: #router
      - podSelector:
          matchLabels:
            component: mysqlrouter
            app.kubernetes.io/instance: mysql-innodbcluster-{{ .Release.Name }}-router
      ports:
        - port: 3306
          protocol: TCP
        - port: 33060
          protocol: TCP
    - from: #peers
      - podSelector:
          matchLabels:
            app.kubernetes.io/component: database
            app.kubernetes.io/created-by: mysql-operator
            app.kubernetes.io/managed-by: mysql-operator
            app.kubernetes.io/name: mysql-innodbcluster-mysql-server
            app.kubernetes.io/instance: mysql-innodbcluster-{{ .Release.Name }}-mysql-server
      ports:
        - port: 3306
          protocol: TCP
    - from: #backup snapshot jobs
        - podSelector:
            matchLabels:
              backupjob: {{ .Release.Name }}
      ports:
        - port: 3306
          protocol: TCP
  podSelector:
    matchLabels:
      app.kubernetes.io/component: database
      app.kubernetes.io/created-by: mysql-operator
      app.kubernetes.io/managed-by: mysql-operator
      app.kubernetes.io/name: mysql-innodbcluster-mysql-server
      app.kubernetes.io/instance: mysql-innodbcluster-{{ .Release.Name }}-mysql-server

(and add stuff for loading backups too?)

for the router:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: {{ .Release.Name }}-router
  labels:
    release: {{ .Release.Name }}
spec:
  policyTypes:
    - Egress
    - Ingress
  egress:
    - to: #cluster
      - podSelector:
          matchLabels:
            app.kubernetes.io/component: database
            app.kubernetes.io/created-by: mysql-operator
            app.kubernetes.io/managed-by: mysql-operator
            app.kubernetes.io/name: mysql-innodbcluster-mysql-server
            app.kubernetes.io/instance: mysql-innodbcluster-{{ .Release.Name }}-mysql-server
      ports:
        - port: 3306
          protocol: TCP
        - port: 33060
          protocol: TCP
        - port: 33061
          protocol: TCP
  ingress:
    - from: #clients
      - podSelector:
          matchLabels:
            {{ .Release.Name }}-client: "true"
      ports:
        - port: 6446 #RW Classic
          protocol: TCP
        - port: 6447 #R Classic
          protocol: TCP
        - port: 6448 #RW X
          protocol: TCP
        - port: 6449 #R X
          protocol: TCP
        - port: 6450 #RW split connections Classic
          protocol: TCP
  podSelector:
    matchLabels:
      app.kubernetes.io/component: router
      app.kubernetes.io/created-by: mysql-operator
      app.kubernetes.io/managed-by: mysql-operator
      app.kubernetes.io/name: mysql-router
      app.kubernetes.io/instance: mysql-innodbcluster-{{ .Release.Name }}-router

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
{{- end }}
metadata:
  name: {{ .Release.Name }}-backupjob
  labels:
    release: {{ .Release.Name }}
spec:
  policyTypes:
    - Egress
  egress:
    - to: #apiserver
      {{- range .Values.netpol.ApiServerIps }}
      - ipBlock:
          cidr: {{ . }}/32
      {{- end }}
      - namespaceSelector:
          matchLabels:
            name: kube-system
        podSelector:
          matchLabels:
            component: kube-apiserver
            tier: control-plane
      ports:
        - port: {{ .Values.netpol.apiPort }}
    - to: #cluster
      - podSelector:
          matchLabels:
            app.kubernetes.io/component: database
            app.kubernetes.io/created-by: mysql-operator
            app.kubernetes.io/managed-by: mysql-operator
            app.kubernetes.io/name: mysql-innodbcluster-mysql-server
            app.kubernetes.io/instance: mysql-innodbcluster-{{ .Release.Name }}-mysql-server
      ports:
        - port: 3306
          protocol: TCP
        - port: 33060
          protocol: TCP
        - port: 33061
          protocol: TCP
  podSelector:
    matchLabels:
      backupjob: {{ .Release.Name }}
+logic for fetching the backups from s3, oci, ...
[30 Dec 2024 14:02] MySQL Verification Team
Hello Martin Fleurke,

Thank you for the feature request!

regards,
Umesh