kind: ConfigMap
apiVersion: v1
metadata:
  namespace: openshift-sdn
  name: sdn-config
data:
  kube-proxy-config.yaml: |-
{{.KubeProxyConfig | indent 4}}
---
kind: DaemonSet
apiVersion: apps/v1
metadata:
  name: sdn
  namespace: openshift-sdn
  annotations:
    kubernetes.io/description: |
      This daemon set launches the OpenShift networking components (kube-proxy and openshift-sdn).
      It expects that OVS is running on the node.
    release.openshift.io/version: "{{.ReleaseVersion}}"
spec:
  selector:
    matchLabels:
      app: sdn
  updateStrategy:
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: sdn
        component: network
        type: infra
        openshift.io/component: network
    spec:
      # Requires fairly broad permissions - ability to read all services and network functions as well
      # as all pods.
      serviceAccountName: sdn
      hostNetwork: true
      hostPID: true
      priorityClassName: system-node-critical
      containers:
      - name: sdn
        image: {{.SDNImage}}
        command: 
        - /bin/bash
        - -c
        - |
          #!/bin/bash
          set -euo pipefail

          # if another process is listening on the cni-server socket, wait until it exits
          trap 'kill $(jobs -p); rm -f /etc/cni/net.d/80-openshift-network.conf ; exit 0' TERM
          retries=0
          while true; do
            if echo 'test' | socat - UNIX-CONNECT:/var/run/openshift-sdn/cniserver/socket &>/dev/null; then
              echo "warning: Another process is currently listening on the CNI socket, waiting 15s ..." 2>&1
              sleep 15 & wait
              (( retries += 1 ))
            else
              break
            fi
            if [[ "${retries}" -gt 40 ]]; then
              echo "error: Another process is currently listening on the CNI socket, exiting" 2>&1
              exit 1
            fi
          done

          # local environment overrides
          if [[ -f /etc/sysconfig/openshift-sdn ]]; then
            set -o allexport
            source /etc/sysconfig/openshift-sdn
            set +o allexport
          fi
          #BUG: cdc accidentally mounted /etc/sysconfig/openshift-sdn as DirectoryOrCreate; clean it up so we can ultimately mount /etc/sysconfig/openshift-sdn as FileOrCreate
          # Once this is released, then we can mount it properly
          if [[ -d /etc/sysconfig/openshift-sdn ]]; then
            rmdir /etc/sysconfig/openshift-sdn || true
          fi

          # configmap-based overrides
          if [[ -f /env/${K8S_NODE_NAME} ]]; then
            set -o allexport
            source /env/${K8S_NODE_NAME}
            set +o allexport
          fi

          # Take over network functions on the node
          rm -f /etc/cni/net.d/80-openshift-network.conf
          cp -f /opt/cni/bin/openshift-sdn /host/opt/cni/bin/

          # Launch the network process
          exec /usr/bin/openshift-sdn-node \
            --node-name ${K8S_NODE_NAME} --node-ip ${K8S_NODE_IP} \
            --proxy-config /config/kube-proxy-config.yaml \
            --v ${OPENSHIFT_SDN_LOG_LEVEL:-2}
        securityContext:
          privileged: true
        volumeMounts:
        - mountPath: /config
          name: config
          readOnly: true
        - mountPath: /env
          name: env-overrides
        # Mount the entire run directory for socket access for Docker or CRI-o
        # TODO: remove
        - mountPath: /var/run
          name: host-var-run
        # Run directories where we need to be able to access sockets
        - mountPath: /var/run/dbus/
          name: host-var-run-dbus
          readOnly: true
        - mountPath: /var/run/openvswitch/
          name: host-var-run-ovs
          readOnly: true
        - mountPath: /var/run/kubernetes/
          name: host-var-run-kubernetes
          readOnly: true
        # accessing bind-mounted net namespaces
        - mountPath: /run/netns
          name: host-run-netns
          readOnly: true
          mountPropagation: HostToContainer
        - name: host-var-run-netns
          mountPath: /host/var/run/netns
          mountPropagation: HostToContainer
          readOnly: true
        # We mount our socket here
        - mountPath: /var/run/openshift-sdn
          name: host-var-run-openshift-sdn
        - mountPath: /host
          name: host-slash
          readOnly: true
          mountPropagation: HostToContainer
        # CNI related mounts which we take over
        - mountPath: /host/opt/cni/bin
          name: host-cni-bin
        - mountPath: /etc/cni/net.d
          name: host-cni-conf
        - mountPath: /var/lib/cni/networks/openshift-sdn
          name: host-var-lib-cni-networks-openshift-sdn
        # If iptables needs to load a module
        - mountPath: /lib/modules
          name: host-modules
          readOnly: true
        - mountPath: /etc/sysconfig
          name: etc-sysconfig
          readOnly: true
        resources:
          requests:
            cpu: 100m
            memory: 200Mi
        env:
        # point openshift-sdn to the internal apiserver load balancer
        - name: KUBERNETES_SERVICE_PORT
          value: "{{.KUBERNETES_SERVICE_PORT}}"
        - name: KUBERNETES_SERVICE_HOST
          value: "{{.KUBERNETES_SERVICE_HOST}}"
        - name: OPENSHIFT_DNS_DOMAIN
          value: cluster.local
        - name: K8S_NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: K8S_NODE_IP
          valueFrom:
            fieldRef:
              fieldPath: status.hostIP
        ports:
        - name: healthz
          containerPort: 10256
        terminationMessagePolicy: FallbackToLogsOnError
        lifecycle:
          preStop:
            exec:
              command: ["rm","-f","/etc/cni/net.d/80-openshift-network.conf", "/host/opt/cni/bin/openshift-sdn"]
        readinessProbe:
          exec:
            # openshift-sdn writes this file when it is ready to handle pod requests.
            command: ["test", "-f", "/etc/cni/net.d/80-openshift-network.conf"]
          initialDelaySeconds: 5
          periodSeconds: 5
      - name: kube-rbac-proxy
        image: {{.KubeRBACProxyImage}}
        command:
        - /bin/bash
        - -c
        - |
          #!/bin/bash
          set -euo pipefail
          TLS_PK=/etc/pki/tls/metrics-certs/tls.key
          TLS_CERT=/etc/pki/tls/metrics-certs/tls.crt

          # As the secret mount is optional we must wait for the files to be present.
          # The service is created in monitor.yaml and this is created in sdn.yaml.
          # If it isn't created there is probably an issue so we want to crashloop.
          TS=$(date +%s)
          WARN_TS=$(( ${TS} + $(( 20 * 60)) ))
          HAS_LOGGED_INFO=0
          
          log_missing_certs(){
              CUR_TS=$(date +%s)
              if [[ "${CUR_TS}" -gt "WARN_TS"  ]]; then
                echo $(date -Iseconds) WARN: sdn-metrics-certs not mounted after 20 minutes.
              elif [[ "${HAS_LOGGED_INFO}" -eq 0 ]] ; then
                echo $(date -Iseconds) INFO: sdn-metrics-certs not mounted. Waiting 20 minutes.
                HAS_LOGGED_INFO=1
              fi
          }

          while [[ ! -f "${TLS_PK}" ||  ! -f "${TLS_CERT}" ]] ; do
            log_missing_certs
            sleep 5
          done

          echo $(date -Iseconds) INFO: sdn-metrics-certs mounted, starting kube-rbac-proxy
          exec /usr/bin/kube-rbac-proxy \
            --logtostderr \
            --secure-listen-address=:9101 \
            --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:29101/ \
            --tls-private-key-file=${TLS_PK} \
            --tls-cert-file=${TLS_CERT}
        ports:
        - containerPort: 9101
          name: https
        resources:
          requests:
            cpu: 10m
            memory: 20Mi
        terminationMessagePolicy: FallbackToLogsOnError
        volumeMounts:
        - name: sdn-metrics-certs
          mountPath: /etc/pki/tls/metrics-certs
          readOnly: True
      {{- if .SDNPlatformAzure}}
      - name: drop-icmp
        image: {{.SDNImage}}
        command:
        - /bin/bash
        - -c
        - |
          set -xe

          touch /var/run/add_iptables.sh
          chmod 0755 /var/run/add_iptables.sh
          cat <<'EOF' > /var/run/add_iptables.sh
          #!/bin/sh
          if [ -z "$3" ]
          then
               echo "Called with host address missing, ignore"
               exit 0
          fi
          echo "Adding ICMP drop rule for '$3' "
          if iptables -C AZURE_CHECK_ICMP_SOURCE -p icmp -s $3 -j AZURE_ICMP_ACTION
          then
               echo "iptables already set for $3"
          else
               iptables -A AZURE_CHECK_ICMP_SOURCE -p icmp -s $3 -j AZURE_ICMP_ACTION
          fi
          EOF

          echo "I$(date "+%m%d %H:%M:%S.%N") - drop-icmp - start drop-icmp ${K8S_NODE}"
          iptables -X AZURE_CHECK_ICMP_SOURCE || true
          iptables -N AZURE_CHECK_ICMP_SOURCE || true
          iptables -F AZURE_CHECK_ICMP_SOURCE
          iptables -D INPUT -p icmp --icmp-type fragmentation-needed -j AZURE_CHECK_ICMP_SOURCE || true
          iptables -I INPUT -p icmp --icmp-type fragmentation-needed -j AZURE_CHECK_ICMP_SOURCE
          iptables -N AZURE_ICMP_ACTION || true
          iptables -F AZURE_ICMP_ACTION
          iptables -A AZURE_ICMP_ACTION -j LOG
          iptables -A AZURE_ICMP_ACTION -j DROP
          /host/usr/bin/oc observe pods -n openshift-sdn -l app=sdn -a '{ .status.hostIP }' -- /var/run/add_iptables.sh
        lifecycle:
          preStop:
            exec:
              command: ["/bin/bash", "-c", "echo drop-icmp done"]
        securityContext:
          privileged: true
        volumeMounts:
        - mountPath: /host
          name: host-slash
        resources:
          requests:
            cpu: 5m
            memory: 20Mi
        env:
        - name: K8S_NODE
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
      {{- end}}
      nodeSelector:
        kubernetes.io/os: linux
      volumes:
      - name: config
        configMap:
          name: sdn-config
      - name: env-overrides
        configMap:
          name: env-overrides
          optional: true
      - name: etc-sysconfig
        hostPath:
          path: /etc/sysconfig
      - name: host-modules
        hostPath:
          path: /lib/modules
      # TODO: access to the docker socket should be replaced by CRI socket
      - name: host-var-run
        hostPath:
          path: /var/run
      - name: host-run-netns
        hostPath:
          path: /run/netns
      - name: host-var-run-netns
        hostPath:
          path: /var/run/netns
      - name: host-var-run-dbus
        hostPath:
          path: /var/run/dbus
      - name: host-var-run-ovs
        hostPath:
          path: /var/run/openvswitch
      - name: host-var-run-kubernetes
        hostPath:
          path: /var/run/kubernetes
      - name: host-var-run-openshift-sdn
        hostPath:
          path: /var/run/openshift-sdn
      - name: host-slash
        hostPath:
          path: /
      - name: host-cni-bin
        hostPath:
          path: {{.CNIBinDir}}
      - name: host-cni-conf
        hostPath:
          path: {{.CNIConfDir}}
      - name: host-var-lib-cni-networks-openshift-sdn
        hostPath:
          path: /var/lib/cni/networks/openshift-sdn
      # Must be optional because the sdn-metrics-certs is a service serving
      # certificate and those cannot be generated without the SDN running
      - name: sdn-metrics-certs
        secret:
          secretName: sdn-metrics-certs
          optional: true
      tolerations:
      - operator: Exists
