apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  labels:
    app: metallb
  name: controller
spec:
  allowPrivilegeEscalation: false
  allowedCapabilities: []
  allowedHostPaths: []
  defaultAddCapabilities: []
  defaultAllowPrivilegeEscalation: false
  fsGroup:
    ranges:
      - max: 65535
        min: 1
    rule: MustRunAs
  hostIPC: false
  hostNetwork: false
  hostPID: false
  privileged: false
  readOnlyRootFilesystem: true
  requiredDropCapabilities:
    - ALL
  runAsUser:
    ranges:
      - max: 65535
        min: 1
    rule: MustRunAs
  seLinux:
    rule: RunAsAny
  supplementalGroups:
    ranges:
      - max: 65535
        min: 1
    rule: MustRunAs
  volumes:
    - configMap
    - secret
    - emptyDir
---
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  labels:
    app: metallb
  name: speaker
spec:
  allowPrivilegeEscalation: false
  allowedCapabilities:
    - NET_RAW
  allowedHostPaths: []
  defaultAddCapabilities: []
  defaultAllowPrivilegeEscalation: false
  fsGroup:
    rule: RunAsAny
  hostIPC: false
  hostNetwork: true
  hostPID: false
  hostPorts:
    - max: {{.MetricsPort}}
      min: {{.MetricsPort}}
    - max: 7946
      min: 7946
  privileged: true
  readOnlyRootFilesystem: true
  requiredDropCapabilities:
    - ALL
  runAsUser:
    rule: RunAsAny
  seLinux:
    rule: RunAsAny
  supplementalGroups:
    rule: RunAsAny
  volumes:
    - configMap
    - secret
    - emptyDir
---
apiVersion: v1
data:
  daemons: |
    # This file tells the frr package which daemons to start.
    #
    # Sample configurations for these daemons can be found in
    # /usr/share/doc/frr/examples/.
    #
    # ATTENTION:
    #
    # When activating a daemon for the first time, a config file, even if it is
    # empty, has to be present *and* be owned by the user and group "frr", else
    # the daemon will not be started by /etc/init.d/frr. The permissions should
    # be u=rw,g=r,o=.
    # When using "vtysh" such a config file is also needed. It should be owned by
    # group "frrvty" and set to ug=rw,o= though. Check /etc/pam.d/frr, too.
    #
    # The watchfrr and zebra daemons are always started.
    #
    bgpd=yes
    ospfd=no
    ospf6d=no
    ripd=no
    ripngd=no
    isisd=no
    pimd=no
    ldpd=no
    nhrpd=no
    eigrpd=no
    babeld=no
    sharpd=no
    pbrd=no
    bfdd=yes
    fabricd=no
    vrrpd=no

    #
    # If this option is set the /etc/init.d/frr script automatically loads
    # the config via "vtysh -b" when the servers are started.
    # Check /etc/pam.d/frr if you intend to use "vtysh"!
    #
    vtysh_enable=yes
    zebra_options="  -A 127.0.0.1 -s 90000000"
    bgpd_options="   -A 127.0.0.1 -p 0"
    ospfd_options="  -A 127.0.0.1"
    ospf6d_options=" -A ::1"
    ripd_options="   -A 127.0.0.1"
    ripngd_options=" -A ::1"
    isisd_options="  -A 127.0.0.1"
    pimd_options="   -A 127.0.0.1"
    ldpd_options="   -A 127.0.0.1"
    nhrpd_options="  -A 127.0.0.1"
    eigrpd_options=" -A 127.0.0.1"
    babeld_options=" -A 127.0.0.1"
    sharpd_options=" -A 127.0.0.1"
    pbrd_options="   -A 127.0.0.1"
    staticd_options="-A 127.0.0.1"
    bfdd_options="   -A 127.0.0.1"
    fabricd_options="-A 127.0.0.1"
    vrrpd_options="  -A 127.0.0.1"

    # configuration profile
    #
    #frr_profile="traditional"
    #frr_profile="datacenter"

    #
    # This is the maximum number of FD's that will be available.
    # Upon startup this is read by the control files and ulimit
    # is called. Uncomment and use a reasonable value for your
    # setup if you are expecting a large number of peers in
    # say BGP.
    #MAX_FDS=1024

    # The list of daemons to watch is automatically generated by the init script.
    #watchfrr_options=""

    # for debugging purposes, you can specify a "wrap" command to start instead
    # of starting the daemon directly, e.g. to use valgrind on ospfd:
    #   ospfd_wrap="/usr/bin/valgrind"
    # or you can use "all_wrap" for all daemons, e.g. to use perf record:
    #   all_wrap="/usr/bin/perf record --call-graph -"
    # the normal daemon command is added to this at the end.
  frr.conf: |
    ! This file gets overriden the first time the speaker renders a config.
    ! So anything configured here is only temporary.
    frr version 7.5.1
    frr defaults traditional
    hostname Router
    line vty
    log file /etc/frr/frr.log informational
  vtysh.conf: |
    service integrated-vtysh-config
kind: ConfigMap
metadata:
  name: frr-startup
  namespace: '{{.NameSpace}}'
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: metallb
    component: controller
  name: controller
  namespace: '{{.NameSpace}}'
spec:
  revisionHistoryLimit: 3
  selector:
    matchLabels:
      app: metallb
      component: controller
  template:
    metadata:
      annotations:
        prometheus.io/port: "{{.MetricsPort}}"
        prometheus.io/scrape: "true"
      labels:
        app: metallb
        component: controller
    spec:
      containers:
        - args:
            - --webhook-mode=disabled
            - --port={{.MetricsPort}}
            - --log-level={{.LogLevel}}
            - --webhook-mode=disabled
          env:
            - name: METALLB_BGP_TYPE
              value: frr
            - name: METALLB_ML_SECRET_NAME
              value: memberlist
            - name: METALLB_DEPLOYMENT
              value: controller
          image: '{{.ControllerImage}}'
          livenessProbe:
            failureThreshold: 3
            httpGet:
              path: /metrics
              port: monitoring
            initialDelaySeconds: 10
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 1
          name: controller
          ports:
            - containerPort: {{.MetricsPort}}
              name: monitoring
            - containerPort: 9443
              name: webhook-server
              protocol: TCP
          readinessProbe:
            failureThreshold: 3
            httpGet:
              path: /metrics
              port: monitoring
            initialDelaySeconds: 10
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 1
          securityContext:
            allowPrivilegeEscalation: false
            capabilities:
              drop:
                - all
            readOnlyRootFilesystem: true
          command:
            - /controller
        {{ if .DeployKubeRbacProxies }}
        - name: kube-rbac-proxy
          image: '{{.KubeRbacProxy}}'
          imagePullPolicy: IfNotPresent
          args:
            - --logtostderr
            - --secure-listen-address=:{{.MetricsPortHttps}}
            - --tls-cipher-suites=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
            - --upstream=http://127.0.0.1:{{.MetricsPort}}/
            - --tls-private-key-file=/etc/metrics/tls.key
            - --tls-cert-file=/etc/metrics/tls.crt
          ports:
            - containerPort: {{.MetricsPortHttps}}
              name: https-metrics
          resources:
            requests:
              cpu: 10m
              memory: 20Mi
          terminationMessagePolicy: FallbackToLogsOnError
          volumeMounts:
            - name: controller-certs
              mountPath: /etc/metrics
              readOnly: true
        {{ end }}
      nodeSelector:
        kubernetes.io/os: linux
      securityContext:
        runAsNonRoot: true
        {{ if not .IsOpenShift }}
        runAsUser: 65534
        {{ end }}
      serviceAccountName: controller
      terminationGracePeriodSeconds: 0
      volumes:
        {{ if .DeployKubeRbacProxies }}
        - name: controller-certs
          secret:
            secretName: controller-certs-secret
        {{ end }}
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  labels:
    app: metallb
    component: speaker
  name: speaker
  namespace: '{{.NameSpace}}'
spec:
  selector:
    matchLabels:
      app: metallb
      component: speaker
  template:
    metadata:
      annotations:
        prometheus.io/port: "{{.MetricsPort}}"
        prometheus.io/scrape: "true"
      labels:
        app: metallb
        component: speaker
    spec:
      containers:
        - command:
            - /bin/sh
            - -c
            - |
              /sbin/tini -- /usr/lib/frr/docker-start &
              attempts=0
              until [[ -f /etc/frr/frr.log || $attempts -eq 60 ]]; do
                sleep 1
                attempts=$(( $attempts + 1 ))
              done
              tail -f /etc/frr/frr.log
          env:
            - name: TINI_SUBREAPER
              value: "true"
          image: '{{.FRRImage}}'
          name: frr
          securityContext:
            capabilities:
              add:
                - NET_ADMIN
                - NET_RAW
                - SYS_ADMIN
                - NET_BIND_SERVICE
          volumeMounts:
            - mountPath: /var/run/frr
              name: frr-sockets
            - mountPath: /etc/frr
              name: frr-conf
        - command:
            - /etc/frr_reloader/frr-reloader.sh
          image: '{{.FRRImage}}'
          name: reloader
          volumeMounts:
            - mountPath: /var/run/frr
              name: frr-sockets
            - mountPath: /etc/frr
              name: frr-conf
            - mountPath: /etc/frr_reloader
              name: reloader
        - args:
            - --metrics-port={{.FRRMetricsPort}}
          command:
            - /etc/frr_metrics/frr-metrics
          image: '{{.FRRImage}}'
          name: frr-metrics
          ports:
            - containerPort: {{.FRRMetricsPort}}
              name: monitoring
          volumeMounts:
            - mountPath: /var/run/frr
              name: frr-sockets
            - mountPath: /etc/frr
              name: frr-conf
            - mountPath: /etc/frr_metrics
              name: metrics
        - args:
            - --port={{.MetricsPort}}
            - --log-level={{.LogLevel}}
            - --ml-bindport={{.MLBindPort}}
          env:
            - name: FRR_CONFIG_FILE
              value: /etc/frr_reloader/frr.conf
            - name: FRR_RELOADER_PID_FILE
              value: /etc/frr_reloader/reloader.pid
            - name: METALLB_BGP_TYPE
              value: frr
            - name: METALLB_NODE_NAME
              valueFrom:
                fieldRef:
                  fieldPath: spec.nodeName
            - name: METALLB_HOST
              valueFrom:
                fieldRef:
                  fieldPath: status.hostIP
            - name: METALLB_ML_BIND_ADDR
              valueFrom:
                fieldRef:
                  fieldPath: status.podIP
            - name: METALLB_ML_LABELS
              value: app=metallb,component=speaker
            - name: METALLB_ML_SECRET_KEY_PATH
              value: /etc/ml_secret_key
          image: '{{.SpeakerImage}}'
          livenessProbe:
            failureThreshold: 3
            httpGet:
              path: /metrics
              port: monitoring
            initialDelaySeconds: 10
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 1
          name: speaker
          ports:
            - containerPort: {{.MetricsPort}}
              name: monitoring
            - containerPort: 7946
              name: memberlist-tcp
            - containerPort: 7946
              name: memberlist-udp
              protocol: UDP
          readinessProbe:
            failureThreshold: 3
            httpGet:
              path: /metrics
              port: monitoring
            initialDelaySeconds: 10
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 1
          securityContext:
            allowPrivilegeEscalation: false
            capabilities:
              add:
                - NET_RAW
              drop:
                - ALL
            readOnlyRootFilesystem: true
          volumeMounts:
            - mountPath: /etc/frr_reloader
              name: reloader
            - mountPath: /etc/ml_secret_key
              name: memberlist
              readOnly: true
          command:
            - /speaker
        {{ if .DeployKubeRbacProxies }}
        - name: kube-rbac-proxy-frr
          image: '{{.KubeRbacProxy}}'
          imagePullPolicy: IfNotPresent
          args:
            - --logtostderr
            - --secure-listen-address=:{{.FRRMetricsPortHttps}}
            - --tls-cipher-suites=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
            - --upstream=http://127.0.0.1:{{.FRRMetricsPort}}/
            - --tls-private-key-file=/etc/metrics/tls.key
            - --tls-cert-file=/etc/metrics/tls.crt
          ports:
            - containerPort: {{.FRRMetricsPortHttps}}
              name: https-metrics
          resources:
            requests:
              cpu: 10m
              memory: 20Mi
          terminationMessagePolicy: FallbackToLogsOnError
          volumeMounts:
            - name: speaker-certs
              mountPath: /etc/metrics
              readOnly: true
        - name: kube-rbac-proxy
          image: '{{.KubeRbacProxy}}'
          imagePullPolicy: IfNotPresent
          args:
            - --logtostderr
            - --secure-listen-address=:{{.MetricsPortHttps}}
            - --tls-cipher-suites=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
            - --upstream=http://$(METALLB_HOST):{{.MetricsPort}}/
            - --tls-private-key-file=/etc/metrics/tls.key
            - --tls-cert-file=/etc/metrics/tls.crt
          ports:
            - containerPort: {{.MetricsPortHttps}}
              name: https-metrics
          env:
            - name: METALLB_HOST
              valueFrom:
                fieldRef:
                  fieldPath: status.hostIP
          resources:
            requests:
              cpu: 10m
              memory: 20Mi
          terminationMessagePolicy: FallbackToLogsOnError
          volumeMounts:
            - name: speaker-certs
              mountPath: /etc/metrics
              readOnly: true
        {{ end }}
      hostNetwork: true
      initContainers:
        - command:
            - /bin/sh
            - -c
            - cp -rLf /tmp/frr/* /etc/frr/
          image: '{{.FRRImage}}'
          name: cp-frr-files
          volumeMounts:
            - mountPath: /tmp/frr
              name: frr-startup
            - mountPath: /etc/frr
              name: frr-conf
        - command:
            - /bin/sh
            - -c
            - cp -f /frr-reloader.sh /etc/frr_reloader/
          image: '{{.SpeakerImage}}'
          name: cp-reloader
          volumeMounts:
            - mountPath: /etc/frr_reloader
              name: reloader
        - command:
            - /bin/sh
            - -c
            - cp -f /frr-metrics /etc/frr_metrics/
          image: '{{.SpeakerImage}}'
          name: cp-metrics
          volumeMounts:
            - mountPath: /etc/frr_metrics
              name: metrics
      nodeSelector:
        kubernetes.io/os: linux
      serviceAccountName: speaker
      shareProcessNamespace: true
      terminationGracePeriodSeconds: 2
      tolerations:
        - effect: NoSchedule
          key: node-role.kubernetes.io/master
          operator: Exists
        - effect: NoSchedule
          key: node-role.kubernetes.io/control-plane
          operator: Exists
      volumes:
        - emptyDir: {}
          name: frr-sockets
        - configMap:
            name: frr-startup
          name: frr-startup
        - emptyDir: {}
          name: frr-conf
        - emptyDir: {}
          name: reloader
        - emptyDir: {}
          name: metrics
        - name: memberlist
          secret:
            secretName: memberlist
            defaultMode: 420
        {{ if .DeployKubeRbacProxies }}
        - name: speaker-certs
          secret:
            secretName: speaker-certs-secret
        {{ end }}
