---
controls:
version: rh-1.8
id: 1
text: "Control Plane Components"
type: "master"
groups:
  - id: 1.1
    text: "Master Node Configuration Files"
    checks:
      - id: 1.1.1
        text: "Ensure that the API server pod specification file permissions are set to 600 or more restrictive (Manual)"
        audit: |
          # Get the node name where the pod is running
          NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}')

          # Get the pod name in the openshift-kube-apiserver namespace
          POD_NAME=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)

          if [ -z "$POD_NAME" ]; then
              echo "No matching pods found on the current node."
          else
             # Execute the stat command
             oc exec -n openshift-kube-apiserver "$POD_NAME" -- stat -c "$POD_NAME %n permissions=%a" /etc/kubernetes/static-pod-resources/kube-apiserver-pod.yaml
          fi
        use_multiple_values: true
        tests:
          test_items:
            - flag: "permissions"
              compare:
                op: bitmask
                value: "600"
        remediation: |
          No remediation required; file permissions are managed by the operator.
        scored: true

      - id: 1.1.2
        text: "Ensure that the API server pod specification file ownership is set to root:root (Manual)"
        audit: |
          # Get the node name where the pod is running
          NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}')

          # Get the pod name in the openshift-kube-apiserver namespace
          POD_NAME=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)

          if [ -z "$POD_NAME" ]; then
              echo "No matching pods found on the current node."
          else
             # Execute the stat command
             oc exec -n openshift-kube-apiserver "$POD_NAME" -- stat -c "$POD_NAME %n %U:%G" /etc/kubernetes/static-pod-resources/kube-apiserver-pod.yaml
          fi
        use_multiple_values: true
        tests:
          test_items:
            - flag: "root:root"
        remediation: |
          No remediation required; file permissions are managed by the operator.
        scored: true

      - id: 1.1.3
        text: "Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive (Manual)"
        audit: |
          # Get the node name where the pod is running
          NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}')

          # Get the pod name in the openshift-kube-controller-manager namespace
          POD_NAME=$(oc get pods -n openshift-kube-controller-manager -l app=kube-controller-manager --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)

          if [ -z "$POD_NAME" ]; then
             echo "No matching pods found on the current node."
          else
            # Execute the stat command
            oc exec -n openshift-kube-controller-manager "$POD_NAME" -- stat -c "$POD_NAME %n permissions=%a" /etc/kubernetes/static-pod-resources/kube-controller-manager-pod.yaml
          fi
        use_multiple_values: true
        tests:
          test_items:
            - flag: "permissions"
              compare:
                op: bitmask
                value: "600"
        remediation: |
          No remediation required; file permissions are managed by the operator.
        scored: true

      - id: 1.1.4
        text: "Ensure that the controller manager pod specification file ownership is set to root:root (Manual)"
        audit: |
          # Get the node name where the pod is running
          NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}')

          # Get the pod name in the openshift-kube-controller-manager namespace
          POD_NAME=$(oc get pods -n openshift-kube-controller-manager -l app=kube-controller-manager --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)

          if [ -z "$POD_NAME" ]; then
            echo "No matching pods found on the current node."
          else
           # Execute the stat command
           oc exec -n openshift-kube-controller-manager "$POD_NAME" -- stat -c "$POD_NAME %n %U:%G" /etc/kubernetes/static-pod-resources/kube-controller-manager-pod.yaml
          fi
        use_multiple_values: true
        tests:
          test_items:
            - flag: "root:root"
        remediation: |
          No remediation required; file permissions are managed by the operator.
        scored: true

      - id: 1.1.5
        text: "Ensure that the scheduler pod specification file permissions are set to 600 or more restrictive (Manual)"
        audit: |
          # Get the node name where the pod is running
          NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}')

          # Get the pod name in the openshift-kube-scheduler namespace
          POD_NAME=$(oc get pods -n openshift-kube-scheduler -l app=openshift-kube-scheduler --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)

          if [ -z "$POD_NAME" ]; then
           echo "No matching pods found on the current node."
          else
          # Execute the stat command
          oc exec -n openshift-kube-scheduler "$POD_NAME" -- stat -c "$POD_NAME %n permissions=%a" /etc/kubernetes/static-pod-resources/kube-scheduler-pod.yaml
          fi
        use_multiple_values: true
        tests:
          test_items:
            - flag: "permissions"
              compare:
                op: bitmask
                value: "600"
        remediation: |
          No remediation required; file permissions are managed by the operator.
        scored: true

      - id: 1.1.6
        text: "Ensure that the scheduler pod specification file ownership is set to root:root (Manual))"
        audit: |
          # Get the node name where the pod is running
          NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}')

          # Get the pod name in the openshift-kube-scheduler namespace
          POD_NAME=$(oc get pods -n openshift-kube-scheduler -l app=openshift-kube-scheduler --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)

          if [ -z "$POD_NAME" ]; then
           echo "No matching pods found on the current node."
          else
          # Execute the stat command
          oc exec -n openshift-kube-scheduler "$POD_NAME" -- stat -c "$POD_NAME %n %U:%G" /etc/kubernetes/static-pod-resources/kube-scheduler-pod.yaml
          fi
        use_multiple_values: true
        tests:
          test_items:
            - flag: "root:root"
        remediation: |
          No remediation required; file permissions are managed by the operator.
        scored: true

      - id: 1.1.7
        text: "Ensure that the etcd pod specification file permissions are set to 600 or more restrictive (Manual))"
        audit: |
          # Get the node name where the pod is running
          NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}')

          # Get the pod name in the openshift-etcd namespace
          POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)

          if [ -z "$POD_NAME" ]; then
           echo "No matching pods found on the current node."
          else
          # Execute the stat command
          oc rsh -n openshift-etcd "$POD_NAME" stat -c "$POD_NAME %n permissions=%a" /etc/kubernetes/manifests/etcd-pod.yaml
          fi
        use_multiple_values: true
        tests:
          test_items:
            - flag: "permissions"
              compare:
                op: bitmask
                value: "600"
        remediation: |
          No remediation required; file permissions are managed by the operator.
        scored: true

      - id: 1.1.8
        text: "Ensure that the etcd pod specification file ownership is set to root:root (Manual)"
        audit: |
          # Get the node name where the pod is running
          NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}')

          # Get the pod name in the openshift-etcd namespace
          POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)

          if [ -z "$POD_NAME" ]; then
           echo "No matching pods found on the current node."
          else
          # Execute the stat command
          oc rsh -n openshift-etcd "$POD_NAME" stat -c "$POD_NAME %n %U:%G" /etc/kubernetes/manifests/etcd-pod.yaml
          fi
        use_multiple_values: true
        tests:
          test_items:
            - flag: "root:root"
        remediation: |
          No remediation required; file permissions are managed by the operator.
        scored: true

      - id: 1.1.9
        text: "Ensure that the Container Network Interface file permissions are set to 600 or more restrictive (Manual)"
        audit: |
          # Get the node name where the pod is running
          NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}')
          # For CNI multus
          # Get the pod name in the openshift-multus namespace
          POD_NAME=$(oc get pods -n openshift-multus -l app=multus --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)

          if [ -z "$POD_NAME" ]; then
           echo "No matching pods found on the current node."
          else
          # Execute the stat command
          oc exec -n openshift-multus "$POD_NAME"  -- /bin/bash -c "stat -c \"$i %n permissions=%a\" /host/etc/cni/net.d/*.conf";  2>/dev/null
          oc exec -n openshift-multus "$POD_NAME"  -- /bin/bash -c "stat -c \"$i %n permissions=%a\" /host/var/run/multus/cni/net.d/*.conf";  2>/dev/null
          fi
          # For SDN pods
          POD_NAME=$(oc get pods -n openshift-sdn -l app=sdn --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)

          if [ -z "$POD_NAME" ]; then
           echo "No matching pods found on the current node."
          else
          # Execute the stat command
          oc exec -n openshift-sdn "$POD_NAME"  -- find /var/lib/cni/networks/openshift-sdn -type f -exec stat -c "$i %n permissions=%a" {} \; 2>/dev/null
          oc exec -n openshift-sdn "$POD_NAME"  -- find /var/run/openshift-sdn -type f -exec stat -c "$i %n permissions=%a" {} \; 2>/dev/null
          fi

          # For OVS pods
          POD_NAME=$(oc get pods -n openshift-sdn -l app=ovs --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)

          if [ -z "$POD_NAME" ]; then
           echo "No matching pods found on the current node."
          else
          # Execute the stat command
          oc exec -n openshift-sdn "$POD_NAME"  -- find /var/run/openvswitch -type f -exec stat -c "$i %n permissions=%a" {} \; 2>/dev/null
          oc exec -n openshift-sdn "$POD_NAME"  -- find /etc/openvswitch -type f -exec stat -c "$i %n permissions=%a" {} \; 2>/dev/null
          oc exec -n openshift-sdn "$POD_NAME"  -- find /run/openvswitch -type f -exec stat -c "$i %n permissions=%a" {} \; 2>/dev/null
          fi
        use_multiple_values: true
        tests:
          test_items:
            - flag: "permissions"
              compare:
                op: bitmask
                value: "600"
        remediation: |
          No remediation required; file permissions are managed by the operator.
        scored: true

      - id: 1.1.10
        text: "Ensure that the Container Network Interface file ownership is set to root:root (Manual)"
        audit: |
          # Get the node name where the pod is running
          NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}')
          # For CNI multus
          # Get the pod name in the openshift-multus namespace
          POD_NAME=$(oc get pods -n openshift-multus -l app=multus --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)

          if [ -z "$POD_NAME" ]; then
           echo "No matching pods found on the current node."
          else
          # Execute the stat command
          oc exec -n openshift-multus "$POD_NAME" -- /bin/bash -c "stat -c '$i %n %U:%G' /host/etc/cni/net.d/*.conf" 2>/dev/null
          oc exec -n openshift-multus $i -- /bin/bash -c "stat -c '$i %n %U:%G' /host/var/run/multus/cni/net.d/*.conf"  2>/dev/null
          fi
          # For SDN pods
          POD_NAME=$(oc get pods -n openshift-sdn -l app=sdn --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)

          if [ -z "$POD_NAME" ]; then
           echo "No matching pods found on the current node."
          else
          # Execute the stat command
          oc exec -n openshift-sdn "$POD_NAME"  -- find /var/lib/cni/networks/openshift-sdn -type f -exec stat -c "$i %n %U:%G" {} \; 2>/dev/null
          oc exec -n openshift-sdn "$POD_NAME"  -- find /var/run/openshift-sdn -type f -exec stat -c "$i %n %U:%G" {} \; 2>/dev/null
          fi
          # For OVS pods in 4.5
          POD_NAME=$(oc get pods -n openshift-sdn -l app=ovs --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)

          if [ -z "$POD_NAME" ]; then
           echo "No matching pods found on the current node."
          else
          # Execute the stat command
          oc exec -n openshift-sdn "$POD_NAME"  -- find /var/run/openvswitch -type f -exec stat -c "$i %n %U:%G" {} \; 2>/dev/null
          oc exec -n openshift-sdn "$POD_NAME"  -- find /etc/openvswitch -type f -exec stat -c "$i %n %U:%G" {} \; 2>/dev/null
          oc exec -n openshift-sdn "$POD_NAME"  -- find /run/openvswitch -type f -exec stat -c "$i %n %U:%G" {} \; 2>/dev/null
          fi
        use_multiple_values: true
        tests:
          test_items:
            - flag: "root:root"
        remediation: |
          No remediation required; file permissions are managed by the operator.
        scored: true

      - id: 1.1.11
        text: "Ensure that the etcd data directory permissions are set to 700 or more restrictive (Manual)"
        audit: |
          # Get the node name where the pod is running
          NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}')

          # Get the pod name in the openshift-etcd namespace
          POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)

          if [ -z "$POD_NAME" ]; then
          echo "No matching pods found on the current node."
          else
          # Execute the stat command
          oc exec -n openshift-etcd "$POD_NAME" -- stat -c "$POD_NAME %n permissions=%a" /var/lib/etcd/member
          fi
        use_multiple_values: true
        tests:
          test_items:
            - flag: "permissions"
              compare:
                op: bitmask
                value: "700"
        remediation: |
          No remediation required; file permissions are managed by the operator.
        scored: true

      - id: 1.1.12
        text: "Ensure that the etcd data directory ownership is set to etcd:etcd (Manual)"
        audit: |
          # Get the node name where the pod is running
          NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}')

          # Get the pod name in the openshift-etcd namespace
          POD_NAME=$(oc get pods -n openshift-etcd -l app=etcd --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)

          if [ -z "$POD_NAME" ]; then
          echo "No matching pods found on the current node."
          else
          # Execute the stat command
          oc exec -n openshift-etcd "$POD_NAME" -- stat -c "$POD_NAME %n %U:%G" /var/lib/etcd/member
          fi
        use_multiple_values: true
        tests:
          test_items:
            - flag: "root:root"
        remediation: |
          No remediation required; file permissions are managed by the operator.
        scored: true

      - id: 1.1.13
        text: "Ensure that the kubeconfig file permissions are set to 600 or more restrictive (Manual)"
        audit: |
          NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}')
          oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n permissions=%a" /etc/kubernetes/kubeconfig 2> /dev/null
        use_multiple_values: true
        tests:
          test_items:
            - flag: "permissions"
              compare:
                op: bitmask
                value: "600"
        remediation: |
          No remediation required; file permissions are managed by the operator.
        scored: true

      - id: 1.1.14
        text: "Ensure that the kubeconfig file ownership is set to root:root (Manual)"
        audit: |
          NODE_NAME=$(oc get pod $HOSTNAME -o=jsonpath='{.spec.nodeName}')
          oc debug node/$NODE_NAME -- chroot /host stat -c "$NODE_NAME %n %U:%G" /etc/kubernetes/kubeconfig 2> /dev/null
        use_multiple_values: true
        tests:
          test_items:
            - flag: "root:root"
        remediation: |
          No remediation required; file permissions are managed by the operator.
        scored: true

      - id: 1.1.15
        text: "Ensure that the scheduler kubeconfig file permissions are set to 600 or more restrictive (Manual)"
        audit: |
          # Get the node name where the pod is running
          NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}')

          # Get the pod name in the openshift-kube-scheduler namespace
          POD_NAME=$(oc get pods -n openshift-kube-scheduler -l app=openshift-kube-scheduler --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)

          if [ -z "$POD_NAME" ]; then
          echo "No matching pods found on the current node."
          else
          # Execute the stat command
          oc exec -n openshift-kube-scheduler "$POD_NAME" -- stat -c "$POD_NAME %n permissions=%a" /etc/kubernetes/static-pod-resources/configmaps/scheduler-kubeconfig/kubeconfig
          fi
        use_multiple_values: true
        tests:
          test_items:
            - flag: "permissions"
              compare:
                op: bitmask
                value: "600"
        remediation: |
          No remediation required; file permissions are managed by the operator.
        scored: true

      - id: 1.1.16
        text: "Ensure that the scheduler kubeconfig file ownership is set to root:root (Manual)"
        audit: |
          # Get the node name where the pod is running
          NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}')

          # Get the pod name in the openshift-kube-scheduler namespace
          POD_NAME=$(oc get pods -n openshift-kube-scheduler -l app=openshift-kube-scheduler --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)

          if [ -z "$POD_NAME" ]; then
          echo "No matching pods found on the current node."
          else
          # Execute the stat command
          oc exec -n openshift-kube-scheduler "$POD_NAME" -- stat -c "$POD_NAME %n %U:%G" /etc/kubernetes/static-pod-resources/configmaps/scheduler-kubeconfig/kubeconfig
          fi
        use_multiple_values: true
        tests:
          test_items:
            - flag: "root:root"
        remediation: |
          No remediation required; file permissions are managed by the operator.
        scored: true

      - id: 1.1.17
        text: "Ensure that the controller-manager kubeconfig file permissions are set to 600 or more restrictive (Manual)"
        audit: |
          # Get the node name where the pod is running
          NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}')

          # Get the pod name in the openshift-kube-controller-manager namespace
          POD_NAME=$(oc get pods -n openshift-kube-controller-manager -l app=kube-controller-manager --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)

          if [ -z "$POD_NAME" ]; then
          echo "No matching pods found on the current node."
          else
          # Execute the stat command
          oc exec -n openshift-kube-controller-manager "$POD_NAME" -- stat -c "$POD_NAME %n permissions=%a" /etc/kubernetes/static-pod-resources/configmaps/controller-manager-kubeconfig/kubeconfig
          fi
        use_multiple_values: true
        tests:
          test_items:
            - flag: "permissions"
              compare:
                op: bitmask
                value: "600"
        remediation: |
          No remediation required; file permissions are managed by the operator.
        scored: true

      - id: 1.1.18
        text: "Ensure that the controller-manager kubeconfig file ownership is set to root:root (Manual)"
        audit: |
          # Get the node name where the pod is running
          NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}')

          # Get the pod name in the openshift-kube-controller-manager namespace
          POD_NAME=$(oc get pods -n openshift-kube-controller-manager -l app=kube-controller-manager --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)

          if [ -z "$POD_NAME" ]; then
          echo "No matching pods found on the current node."
          else
          # Execute the stat command
          oc exec -n openshift-kube-controller-manager "$POD_NAME" -- stat -c "$POD_NAME %n %U:%G" /etc/kubernetes/static-pod-resources/configmaps/controller-manager-kubeconfig/kubeconfig
          fi
        use_multiple_values: true
        tests:
          test_items:
            - flag: "root:root"
        remediation: |
          No remediation required; file permissions are managed by the operator.
        scored: true

      - id: 1.1.19
        text: "Ensure that the OpenShift PKI directory and file ownership is set to root:root (Manual)"
        audit: |
          # Should return root:root for all files and directories
          # Get the node name where the pod is running
          NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}')

          # Get the pod name in the openshift-kube-controller-manager namespace
          POD_NAME=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)

          if [ -z "$POD_NAME" ]; then
          echo "No matching pods found on the current node."
          else
          # echo $i static-pod-certs
          oc exec -n openshift-kube-apiserver "$POD_NAME" -c kube-apiserver -- find /etc/kubernetes/static-pod-certs -type d -wholename '*/secrets*' -exec stat -c "$i %n %U:%G" {} \;
          oc exec -n openshift-kube-apiserver "$POD_NAME" -c kube-apiserver -- find /etc/kubernetes/static-pod-certs -type f -wholename '*/secrets*' -exec stat -c "$i %n %U:%G" {} \;
          # echo $i static-pod-resources
          oc exec -n openshift-kube-apiserver "$POD_NAME" -c kube-apiserver -- find /etc/kubernetes/static-pod-resources -type d -wholename '*/secrets*' -exec stat -c "$i %n %U:%G" {} \;
          oc exec -n openshift-kube-apiserver "$POD_NAME" -c kube-apiserver -- find /etc/kubernetes/static-pod-resources -type f -wholename '*/secrets*' -exec stat -c "$i %n %U:%G" {} \;
          fi
        use_multiple_values: true
        tests:
          test_items:
            - flag: "root:root"
        remediation: |
          No remediation required; file permissions are managed by the operator.
        scored: true

      - id: 1.1.20
        text: "Ensure that the OpenShift PKI certificate file permissions are set to 600 or more restrictive (Manual)"
        audit: |
          # Get the node name where the pod is running
          NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}')

          # Get the pod name in the openshift-kube-apiserver namespace
          POD_NAME=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)

          if [ -z "$POD_NAME" ]; then
          echo "No matching pods found on the current node."
          else
          # Execute the stat command
          oc exec -n openshift-kube-apiserver "$POD_NAME" -c kube-apiserver -- find /etc/kubernetes/static-pod-certs -type f -wholename '*/secrets/*.crt' -exec stat -c "$POD_NAME %n permissions=%a" {} \;
          fi
        use_multiple_values: true
        tests:
          test_items:
            - flag: "permissions"
              compare:
                op: bitmask
                value: "600"
        remediation: |
          No remediation required; file permissions are managed by the operator.
        scored: true

      - id: 1.1.21
        text: "Ensure that the OpenShift PKI key file permissions are set to 600 (Manual)"
        audit: |
          # Get the node name where the pod is running
          NODE_NAME=$(oc get pod "$HOSTNAME" -o=jsonpath='{.spec.nodeName}')

          # Get the pod name in the openshift-kube-apiserver namespace
          POD_NAME=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver --field-selector spec.nodeName="$NODE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)

          if [ -z "$POD_NAME" ]; then
          echo "No matching pods found on the current node."
          else
          # Execute the stat command
          oc exec -n openshift-kube-apiserver "$POD_NAME" -c kube-apiserver -- find /etc/kubernetes/static-pod-certs -type f -wholename '*/secrets/*.key' -exec stat -c "$POD_NAME %n permissions=%a" {} \;
          fi
        use_multiple_values: true
        tests:
          test_items:
            - flag: "permissions"
              compare:
                op: bitmask
                value: "600"
        remediation: |
          No remediation required; file permissions are managed by the operator.
        scored: true

  - id: 1.2
    text: "API Server"
    checks:
      - id: 1.2.1
        text: "Ensure that anonymous requests are authorized (Manual)"
        audit: |
          found=0

          echo "# ClusterRoleBindings granting permissions to system:unauthenticated"
          crb_out="$(oc get clusterrolebindings -o json 2>/dev/null \
            | jq -r '.items[]
              | select(.subjects[]? | select(.kind=="Group" and .name=="system:unauthenticated"))
              | .metadata.name + " -> " + .roleRef.kind + "/" + .roleRef.name' \
            | sort -u)"
          if [ -n "$crb_out" ]; then
            echo "$crb_out"
            found=1
          else
            echo "(none)"
          fi

          echo
          echo "# Namespaced RoleBindings granting permissions to system:unauthenticated"
          rb_out="$(oc get rolebindings -A -o json 2>/dev/null \
            | jq -r '.items[]
              | select(.subjects[]? | select(.kind=="Group" and .name=="system:unauthenticated"))
              | (.metadata.namespace + "/" + .metadata.name) + " -> " + .roleRef.kind + "/" + .roleRef.name' \
            | sort -u)"
          if [ -n "$rb_out" ]; then
            echo "$rb_out"
            found=1
          else
            echo "(none)"
          fi

          # Provide a simple flag for the test harness
          if [ $found -eq 1 ]; then
            echo "unauthenticated_bindings_present"
          else
            echo "unauthenticated_bindings_missing"
          fi
        tests:
          test_items:
            - flag: "unauthenticated_bindings_present"
              set: true
        remediation: |
          None required. The default configuration should not be modified.
        scored: true

      - id: 1.2.2
        text: "Use HTTPS for kubelet connections (Manual)"
        audit: |
          CFG=$(oc -n openshift-kube-apiserver get cm config -o jsonpath='{.data.config\.yaml}')

          # Extract kubelet client cert/key paths (support both layouts)
          CERT_FILE=$(printf '%s\n' "$CFG" \
          | grep -Eo '/etc/kubernetes/static-pod-(resources/kube-apiserver-certs|certs)/secrets/kubelet-client/tls\.crt' \
          | head -n1)

          KEY_FILE=$(printf '%s\n' "$CFG" \
          | grep -Eo '/etc/kubernetes/static-pod-(resources/kube-apiserver-certs|certs)/secrets/kubelet-client/tls\.key' \
          | head -n1)

          # 1) pass/fail on presence of both files
          if [ -n "$CERT_FILE" ] && [ -n "$KEY_FILE" ]; then
            echo "pass"
          else
            echo "fail"
          fi
          KUBELET_HTTPS=$(printf '%s\n' "$CFG" \
            | grep -Eo '(^|[[:space:]])kubelet-https:[[:space:]]*(true|false)' \
            | awk -F: '{print $2}' \
            | tr -d '[:space:]' \
            | head -n1)

          if [ "$KUBELET_HTTPS" = "false" ]; then
            echo "false"
          else
           echo "true"
          fi

          oc -n openshift-apiserver describe secret serving-cert | grep -E 'tls\.crt|tls\.key|Type:'
        tests:
          bin_op: and
          test_items:
            - flag: "pass"
            - flag: "true"
            - flag: "kubernetes.io/tls"
            - flag: "tls.crt"
            - flag: "tls.key"
        remediation: |
          OpenShift does not use the legacy --kubelet-https flag; TLS is enforced via
          kubelet client cert/key arguments and cluster CAs. Ensure:
            - apiServerArguments.kubelet-client-certificate[0] points to a real file
            - apiServerArguments.kubelet-client-key[0] points to a real file
            - The openshift-apiserver 'serving-cert' secret is type kubernetes.io/tls and contains tls.crt and tls.key
        scored: false

      - id: 1.2.3
        text: "Ensure that the kubelet uses certificates to authenticate (Manual)"
        audit: |
          oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments["kubelet-client-certificate"]'
          oc get configmap config -n openshift-kube-apiserver -ojson | jq -r'.data["config.yaml"]' | jq '.apiServerArguments["kubelet-client-key"]'
          oc -n openshift-apiserver describe secret serving-cert
        tests:
          test_items:
            - flag: "/etc/kubernetes/static-pod-certs/secrets/kubelet-client/tls.crt"
            - flag: "/etc/kubernetes/static-pod-certs/secrets/kubelet-client/tls.key"
            - flag: "kubernetes.io/tls"
        remediation: |
          No remediation is required.
          OpenShift automatically manages kubelet authentication using X.509 certificates issued by the internal platform CA.
          Manual modification of these certificates is not supported and can disrupt platform components.
        scored: true

      - id: 1.2.4
        text: "Verify that the kubelet certificate authority is set as appropriate (Manual)"
        audit: |
          oc get configmap config -n openshift-kube-apiserver -ojson \
            | jq -r '.data["config.yaml"]' \
            | jq '.apiServerArguments["kubelet-certificate-authority"]'
        tests:
          test_items:
            - flag: "/etc/kubernetes/static-pod-resources/configmaps/kubelet-serving-ca/ca-bundle.crt"
        remediation: |
          No remediation is required.
          OpenShift uses internal X.509 certificates and platform-managed CAs to verify kubelet server identities.
          This is not user-configurable and should not be modified.
        scored: true

      - id: 1.2.5
        text: "Ensure that the --authorization-mode argument is not set to AlwaysAllow (Manual)"
        audit: |
          oc get configmap config -n openshift-kube-apiserver -o json \
            | jq -r '.data["config.yaml"]' \
            | jq '.apiServerArguments["authorization-mode"]'
        audit_config: |
          oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments'
        tests:
          bin_op: or
          test_items:
            - path: "{.authorization-mode}"
              compare:
                op: nothave
                value: "AlwaysAllow"
            - path: "{.authorization-mode}"
              flag: "authorization-mode"
              set: false
        remediation: |
          No remediation required.
          OpenShift does not support the 'AlwaysAllow' authorization mode.
          The API server is bootstrapped with secure authorization mechanisms including RBAC and Node by default.
        scored: true

      - id: 1.2.6
        text: "Verify that RBAC is enabled (Manual)"
        audit: |
          oc get configmap config -n openshift-kube-apiserver -o json \
            | jq -r '.data["config.yaml"]' \
            | jq '.apiServerArguments["authorization-mode"]'
        audit_config: |
          oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq '.apiServerArguments'
        tests:
          bin_op: or
          test_items:
            - path: "{.authorization-mode}"
              compare:
                op: has
                value: "RBAC"
            - path: "{.authorization-mode}"
              flag: "authorization-mode"
              set: false
        remediation: |
          No remediation is required.
          OpenShift is configured at bootstrap time to use Role-Based Access Control (RBAC) as the default authorization mode.
          RBAC is always enabled, and cannot be disabled through configuration.
        scored: true


      - id: 1.2.7
        text: "Ensure that the APIPriorityAndFairness feature gate is enabled (Manual)"
        audit: |
          oc get kubeapiservers.operator.openshift.io cluster -o json | jq '.spec.observedConfig.apiServerArguments'
        tests:
          test_items:
            - flag: "APIPriorityAndFairness=true"
        remediation: |
          No remediation is required
        scored: true

      - id: 1.2.8
        text: "Ensure that the admission control plugin AlwaysAdmit is not set (Manual)"
        audit: |
          oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"'
        tests:
          test_items:
            - flag: "AlwaysAdmit"
              set: false
        remediation: |
          No remediation is required. The AlwaysAdmit admission controller cannot be enabled in OpenShift.
        scored: true

      - id: 1.2.9
        text: "Ensure that the admission control plugin AlwaysPullImages is set (Manual)"
        audit: |
          oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"'
        tests:
          test_items:
            - flag: "AlwaysPullImages"
              set: false
        remediation: |
          None required.
        scored: true

      - id: 1.2.10
        text: "Ensure that the admission control plugin ServiceAccount is set (Manual)"
        audit: |
          oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"'
        tests:
          test_items:
            - flag: "ServiceAccount"
              set: true
        remediation: |
          None required. OpenShift is configured to use service accounts by default.
        scored: true

      - id: 1.2.11
        text: "Ensure that the admission control plugin NamespaceLifecycle is set (Manual)"
        audit: |
          oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"'
          output=$(oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"')
          [ "$output" == "null" ] && echo "ocp 4.5 has NamespaceLifecycle compiled" || echo $output
        tests:
          test_items:
            - flag: "NamespaceLifecycle"
        remediation: |
          Ensure that the --disable-admission-plugins parameter does not include NamespaceLifecycle.
        scored: true

      - id: 1.2.12
        text: "Ensure that the admission control plugin SecurityContextConstraint is set (Manual)"
        audit: |
          oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"'
          output=$(oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"')
          [ "$output" == "null" ] && echo "ocp 4.5 has SecurityContextConstraint compiled" || echo $output
        tests:
          test_items:
            - flag: "security.openshift.io/SecurityContextConstraint"
        remediation: |
          None required. Security Context Constraints are enabled by default in OpenShift and cannot be disabled.
        scored: true

      - id: 1.2.13
        text: "Ensure that the admission control plugin NodeRestriction is set (Manual)"
        audit: |
          oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"'
          output=$(oc -n openshift-kube-apiserver get configmap config -o json | jq -r '.data."config.yaml"' | jq '.apiServerArguments."enable-admission-plugins"')
          [ "$output" == "null" ] && echo "ocp 4.5 has NodeRestriction compiled" || echo $output
        tests:
          test_items:
            - flag: "NodeRestriction"
        remediation: |
          The NodeRestriction plugin cannot be disabled.
        scored: true

      - id: 1.2.14
        text: "Ensure that the --insecure-bind-address argument is not set (manual)"
        audit: |
          # Get the insecure-bind-address value
          insecure_bind_address=$(oc get kubeapiservers.operator.openshift.io cluster -ojson \
            | jq -r '.spec.observedConfig.apiServerArguments["insecure-bind-address"][]?')

          # Get port from openshift-kube-apiserver
          kube_api_port=$(oc -n openshift-kube-apiserver get endpoints -o jsonpath='{.items[*].subsets[*].ports[*].port}')

          # Get port from openshift-apiserver
          openshift_api_port=$(oc -n openshift-apiserver get endpoints -o jsonpath='{.items[*].subsets[*].ports[*].port}')

          # Evaluate logic
          [[ -z "$insecure_bind_address" ]] && \
          [[ "$kube_api_port" == *"6443"* ]] && \
          [[ "$openshift_api_port" == *"8443"* ]] && echo "pass" || echo "fail"
        tests:
          test_items:
            - flag: "pass"
        remediation: |
          No remediation is required.
          By default, OpenShift uses secure HTTPS ports (6443 and 8443) for all API communications.
          The API servers are not configured to expose insecure ports and are isolated within the pod network.
        scored: true


      - id: 1.2.15
        text: "Ensure that the --insecure-port argument is set to 0 (Manual)"
        audit: |
          oc -n openshift-kube-apiserver get endpoints -o jsonpath='{.items[*].subsets[*].ports[*].port}'
        tests:
          test_items:
            - flag: "6443"
        remediation: |
          None required. The configuration is managed by the API server operator.
        scored: true

      - id: 1.2.16
        text: "Ensure that the --secure-port argument is not set to 0 (Manual)"
        audit: |
          BIND_ADDR=$(oc get kubeapiservers.operator.openshift.io cluster -o json \
            | jq -r '.spec.observedConfig.servingInfo.bindAddress')

          PORTS=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver \
            -o jsonpath='{.items[*].spec.containers[?(@.name=="kube-apiserver")].ports[*].containerPort}')

          if [ "$BIND_ADDR" = "0.0.0.0:6443" ] && echo "$PORTS" | grep -q '\b6443\b'; then
            echo "pass"
          else
            echo "fail"
          fi
        tests:
          test_items:
            - flag: "pass"
        remediation: |
          None required. OpenShift serves the API securely over port 6443 with TLS, authentication, and authorization.
          The insecure API port is not exposed or configurable by default.
        scored: true

      - id: 1.2.17
        text: "Ensure that the healthz endpoint is protected by RBAC (Manual)"
        type: manual
        remediation: |
          None required as profiling data is protected by RBAC.
        scored: false

      - id: 1.2.18
        text: "Ensure that the --audit-log-path argument is set (Manual)"
        audit: |
          # Get kube-apiserver audit log path
          kube_path=$(oc get configmap config -n openshift-kube-apiserver -ojson \
            | jq -r '.data["config.yaml"]' \
            | jq -r '.apiServerArguments["audit-log-path"][]?')

          # Get OpenShift apiserver audit log path
          os_path=$(oc get configmap config -n openshift-apiserver -ojson \
            | jq -r '.data["config.yaml"]' \
            | jq -r '.apiServerArguments["audit-log-path"][]?')

          # Check if log file exists in kube-apiserver pod
          kube_pod=$(oc get pods -n openshift-kube-apiserver -l app=openshift-kube-apiserver -o jsonpath='{.items[0].metadata.name}')
          oc rsh -n openshift-kube-apiserver -c kube-apiserver $kube_pod ls "$kube_path" >/dev/null 2>&1
          kube_exists=$?

          # Check if log file exists in openshift-apiserver pod
          os_pod=$(oc get pods -n openshift-apiserver -l apiserver=true -o jsonpath='{.items[0].metadata.name}')
          oc rsh -n openshift-apiserver $os_pod ls "$os_path" >/dev/null 2>&1
          os_exists=$?

          # Evaluate all conditions
          [[ "$kube_path" == "/var/log/kube-apiserver/audit.log" ]] && \
          [[ "$os_path" == "/var/log/openshift-apiserver/audit.log" ]] && \
          [[ $kube_exists -eq 0 ]] && \
          [[ $os_exists -eq 0 ]] && echo "pass" || echo "fail"
        tests:
          test_items:
            - flag: "pass"
        remediation: |
          No remediation is required.
          OpenShift manages audit logging automatically via the apiserver configuration.
          By default, the audit log paths are:
          - /var/log/kube-apiserver/audit.log
          - /var/log/openshift-apiserver/audit.log
        scored: true

      - id: 1.2.19
        text: "Ensure that the audit logs are forwarded off the cluster for retention (Manual)"
        type: "manual"
        remediation: |
          Follow the documentation for log forwarding. Forwarding logs to third party systems
          https://docs.openshift.com/container-platform/4.5/logging/cluster-logging-external.html
        scored: false

      - id: 1.2.20
        text: "Ensure that the maximumRetainedFiles argument is set to 10 or as appropriate (Manual)"
        audit: |
          VALUE=$(oc get configmap config -n openshift-kube-apiserver -ojson \
            | jq -r '.data["config.yaml"]' \
            | jq -r '.apiServerArguments["audit-log-maxbackup"][0] // empty')

          if [ -n "$VALUE" ] && [ "$VALUE" -ge 10 ]; then
            echo "pass (current=$VALUE)"
          else
            echo "fail (current=$VALUE)"
          fi
        tests:
          test_items:
            - flag: "pass"
        remediation: |
          No remediation required.
          By default, OpenShift retains 10 audit log backup files.
          This provides sufficient log history for incident investigation and audit review.
        scored: true

      - id: 1.2.21
        text: "Configure Kubernetes API Server Maximum Audit Log Size (Manual)"
        audit: |
          VALUE=$(oc get configmap config -n openshift-kube-apiserver -ojson \
            | jq -r '.data["config.yaml"]' \
            | jq -r '.apiServerArguments["audit-log-maxsize"][0] // empty')

          if [ -n "$VALUE" ] && [ "$VALUE" -ge 100 ]; then
            echo "pass (current=$VALUE)"
          else
            echo "fail (current=$VALUE)"
          fi
        tests:
          test_items:
            - flag: "pass"
        remediation: |
          Set the audit-log-maxsize parameter to 100 or as an appropriate number.
          maximumFileSizeMegabytes: 100
        scored: true

      - id: 1.2.22
        text: "Ensure that the --request-timeout argument is set (Manual)"
        audit: |
          VALUE=$(oc get configmap config -n openshift-kube-apiserver -ojson \
            | jq -r '.data["config.yaml"]' \
            | jq -r '.apiServerArguments["min-request-timeout"][0] // empty')

          if [ -n "$VALUE" ] && [ "$VALUE" -eq 3600 ]; then
            echo "pass (current=$VALUE)"
          else
            echo "fail (current=$VALUE)"
          fi
        tests:
          test_items:
            - flag: "pass"
        remediation: |
          TBD
        scored: true

      - id: 1.2.23
        text: "Ensure that the --service-account-lookup argument is set to true (Manual)"
        audit: |
          oc get configmap config -n openshift-kube-apiserver -ojson | jq -r
          '.data["config.yaml"]' | jq '.apiServerArguments."service-account-lookup"[]'
        tests:
          test_items:
            - flag: "true"
        remediation: |
          TBD
        scored: true

      - id: 1.2.24
        text: "Ensure that the --service-account-key-file argument is set as appropriate (Manual)"
        audit: |
          oc get configmap config -n openshift-kube-apiserver -ojson | jq -r '.data["config.yaml"]' | jq -r .serviceAccountPublicKeyFiles[]
        tests:
          bin_op: and
          test_items:
            - flag: "/etc/kubernetes/static-pod-resources/configmaps/sa-token-signing-certs"
            - flag: "/etc/kubernetes/static-pod-resources/configmaps/bound-sa-token-signing-certs"
        remediation: |
          The OpenShift API server does not use the service-account-key-file argument.
          The ServiceAccount token authenticator is configured with serviceAccountConfig.publicKeyFiles.
          OpenShift does not reuse the apiserver TLS key. This is not configurable.
        scored: true

      - id: 1.2.25
        text: "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate (Manual)"
        audit: |
          oc get configmap config -n openshift-kube-apiserver -ojson \
            | jq -r '.data["config.yaml"]' \
            | jq -r '.apiServerArguments["etcd-certfile"][]?'

          oc get configmap config -n openshift-kube-apiserver -ojson \
            | jq -r '.data["config.yaml"]' \
            | jq -r '.apiServerArguments["etcd-keyfile"][]?'
        tests:
          bin_op: and
          test_items:
            - flag: "/etc/kubernetes/static-pod-resources/secrets/etcd-client/tls.crt"
            - flag: "/etc/kubernetes/static-pod-resources/secrets/etcd-client/tls.key"
        remediation: |
          No remediation is required.
          OpenShift automatically manages X.509 client certificates and TLS encryption for secure communication with etcd.
          These settings are handled by the platform and should not be manually modified.
        scored: true

      - id: 1.2.26
        text: "Ensure that the --tls-cert-file and --tls-private-key-file arguments are set as appropriate (Manual)"
        audit: |
          oc get configmap config -n openshift-kube-apiserver -ojson \
            | jq -r '.data["config.yaml"]' \
            | jq -r '.apiServerArguments["tls-cert-file"][]?'

          oc get configmap config -n openshift-kube-apiserver -ojson \
            | jq -r '.data["config.yaml"]' \
            | jq -r '.apiServerArguments["tls-private-key-file"][]?'

        tests:
          bin_op: and
          test_items:
            - flag: "/etc/kubernetes/static-pod-certs/secrets/service-network-serving-certkey/tls.crt"
            - flag: "/etc/kubernetes/static-pod-certs/secrets/service-network-serving-certkey/tls.key"
        remediation: |
          No remediation is required. OpenShift automatically configures the API server with valid X.509 certificates and TLS keys.
          These are used to encrypt traffic between the API server and clients, including kubelets and users.
          Certificate rotation and lifecycle management are handled by the OpenShift platform.
        scored: true


      - id: 1.2.27
        text: "Ensure that the --client-ca-file argument is set as appropriate (Manual)"
        audit: |
          oc get configmap config -n openshift-kube-apiserver -ojson | \
          jq -r '.data["config.yaml"]' | \
          jq -r .servingInfo.clientCA
        tests:
          test_items:
            - flag: "/etc/kubernetes/static-pod-certs/configmaps/client-ca/ca-bundle.crt"
        remediation: |
          OpenShift automatically manages TLS authentication for the API server communication with the node/kublet.
          This is not configurable. You may optionally set a custom default certificate to be used by the API
          server when serving content in order to enable clients to access the API server at a different host name
          or without the need to distribute the cluster-managed certificate authority (CA) certificates to the clients.

          User-provided certificates must be provided in a kubernetes.io/tls type Secret in the openshift-config namespace.
          Update the API server cluster configuration,
          the apiserver/cluster resource, to enable the use of the user-provided certificate.
        scored: true

      - id: 1.2.28
        text: "Ensure that the --etcd-cafile argument is set as appropriate (Manual)"
        audit: |
          oc get configmap config -n openshift-kube-apiserver -ojson | \
            jq -r '.data["config.yaml"]' | \
            jq -r '.apiServerArguments["etcd-cafile"]'
        tests:
          test_items:
            - flag: "/etc/kubernetes/static-pod-resources/configmaps/etcd-serving-ca/ca-bundle.crt"
        remediation: |
          None required. OpenShift generates the etcd-cafile and sets the arguments appropriately in the API server. Communication with etcd is secured by the etcd serving CA.
        scored: true

      - id: 1.2.29
        text: "Ensure that encryption providers are appropriately configured (Manual)"
        audit: |
          oc get openshiftapiserver -o=jsonpath='{range .items[0].status.conditions[?(@.type=="Encrypted")]}{.reason}{"\n"}{.message}{"\n"}'
        tests:
          test_items:
            - flag: "EncryptionCompleted"
        remediation: |
          Follow the Kubernetes documentation and configure a EncryptionConfig file.
          In this file, choose aescbc, kms or secretbox as the encryption provider.
        scored: true

      - id: 1.2.30
        text: "Ensure that the API Server only makes use of Strong Cryptographic Ciphers (Manual)"
        type: manual
        audit: |
          oc get cm -n openshift-authentication v4-0-config-system-cliconfig -o jsonpath='{.data.v4\-0\-config\-system\-cliconfig}' | jq .servingInfo
          oc get kubeapiservers.operator.openshift.io cluster -o json |jq.spec.observedConfig.servingInfo
          oc get openshiftapiservers.operator.openshift.io cluster -o json |jq.spec.observedConfig.servingInfo
          oc describe --namespace=openshift-ingress-operator ingresscontroller/default
        remediation: |
          Verify that the tlsSecurityProfile is set to the value you chose.
          Note: The HAProxy Ingress controller image does not support TLS 1.3
          and because the Modern profile requires TLS 1.3, it is not supported.
          The Ingress Operator converts the Modern profile to Intermediate.
          The Ingress Operator also converts the TLS 1.0 of an Old or Custom profile to 1.1,
          and TLS 1.3 of a Custom profile to 1.2.
        scored: false

      - id: 1.2.31
        text: "Ensure unsupported configuration overrides are not used (Manual)"
        audit: |
          oc get kubeapiserver/cluster -o jsonpath='{.spec.unsupportedConfigOverrides}'
        tests:
          test_items:
            - flag: "null"
        remediation: |
          No remediation is required.
          OpenShift has deprecated and disabled unsupportedConfigOverrides.
          This field should remain null and must not be used in any supported configuration.
        scored: true

  - id: 1.3
    text: "Controller Manager"
    checks:

      - id: 1.3.1
        text: "Ensure that controller manager healthz endpoints are protected by RBAC (Manual)"
        type: manual
        audit: |
          # Verify configuration for ports, livenessProbe, readinessProbe, healthz
          oc -n openshift-kube-controller-manager get cm kube-controller-manager-pod -o json | jq -r '.data."pod.yaml"' | jq '.spec.containers'
          # Verify endpoints
          oc -n openshift-kube-controller-manager describe endpoints
          # Test to validate RBAC enabled on the controller endpoint; check with non-admin role
          oc project openshift-kube-controller-manage
          POD=$(oc get pods -n openshift-kube-controller-manager -l app=kube-controller-manager -o jsonpath='{.items[0].metadata.name}')
          PORT=$(oc get pods -n openshift-kube-controller-manager -l app=kube-controller-manager -o jsonpath='{.items[0].spec.containers[0].ports[0].hostPort}')
          # Following should return 403 Forbidden
          oc rsh -n openshift-kube-controller-manager ${POD} curl https://localhost:${PORT}/metrics -k
          # Create a service account to test RBAC
          oc create -n openshift-kube-controller-manager sa permission-test-sa
          # Should return 403 Forbidden
          SA_TOKEN=$(oc sa -n openshift-kube-controller-manager get-token permission-test-sa)
          oc rsh -n openshift-kube-controller-manager ${POD} curl https://localhost:${PORT}/metrics -H "Authorization: Bearer $SA_TOKEN" -k
          # Cleanup
          oc delete -n openshift-kube-controller-manager sa permission-test-sa
          # As cluster admin, should succeed
          CLUSTER_ADMIN_TOKEN=$(oc whoami -t)
          oc rsh -n openshift-kube-controller-manager ${POD} curl https://localhost:${PORT}/metrics -H "Authorization: Bearer $CLUSTER_ADMIN_TOKEN" -k
        remediation: |
          None required; profiling is protected by RBAC.
        scored: false

      - id: 1.3.2
        text: "Ensure that the --use-service-account-credentials argument is set to true (Manual)"
        audit: |
          oc get configmaps config -n openshift-kube-controller-manager -ojson | \
          jq -r '.data["config.yaml"]' | \
          jq -r '.extendedArguments["use-service-account-credentials"][]'
        tests:
          test_items:
            - flag: "true"
        remediation: |
          The OpenShift Controller Manager operator manages and updates the OpenShift Controller Manager.
          The Kubernetes Controller Manager operator manages and updates the Kubernetes Controller Manager deployed on top of OpenShift.
          This operator is configured via KubeControllerManager custom resource.
        scored: true

      - id: 1.3.3
        text: "Ensure that the --service-account-private-key-file argument is set as appropriate (Manual)"
        audit: |
          oc get configmaps config -n openshift-kube-controller-manager -ojson | \
            jq -r '.data["config.yaml"]' | \
            jq -r '.extendedArguments["service-account-private-key-file"][]'
        tests:
          test_items:
            - flag: "/etc/kubernetes/static-pod-resources/secrets/service-account-private-key/service-account.key"
        remediation: |
          None required.
          OpenShift manages the service account credentials for the scheduler automatically.
        scored: true

      - id: 1.3.4
        text: "Ensure that the --root-ca-file argument is set as appropriate (Manual)"
        audit: |
          oc get configmaps config -n openshift-kube-controller-manager -ojson | \
            jq -r '.data["config.yaml"]' | \
            jq -r '.extendedArguments["root-ca-file"][]'
        tests:
          test_items:
            - flag: "/etc/kubernetes/static-pod-resources/configmaps/serviceaccount-ca/ca-bundle.crt"
        remediation: |
          None required.
          Certificates for OpenShift platform components are automatically created and rotated by the OpenShift Container Platform.
        scored: true


  - id: 1.4
    text: "Scheduler"
    checks:
      - id: 1.4.1
        text: "Ensure that the healthz endpoints for the scheduler are protected by RBAC (Manual)"
        type: manual
        audit: |
          # check configuration for ports, livenessProbe, readinessProbe, healthz
          oc -n openshift-kube-scheduler get cm kube-scheduler-pod -o json | jq -r '.data."pod.yaml"' | jq '.spec.containers'
          # Test to verify endpoints
          oc -n openshift-kube-scheduler describe endpoints
          # Test to validate RBAC enabled on the scheduler endpoint; check with non-admin role
          oc project openshift-kube-scheduler
          POD=$(oc get pods -l app=openshift-kube-scheduler -o jsonpath='{.items[0].metadata.name}')
          PORT=$(oc get pod $POD -o jsonpath='{.spec.containers[0].livenessProbe.httpGet.port}')
          # Should return 403 Forbidden
          oc rsh ${POD} curl http://localhost:${PORT}/metrics -k
          # Create a service account to test RBAC
          oc create sa permission-test-sa
          # Should return 403 Forbidden
          SA_TOKEN=$(oc sa get-token permission-test-sa)
          oc rsh ${POD} curl http://localhost:${PORT}/metrics -H "Authorization: Bearer $SA_TOKEN" -k
          # Cleanup
          oc delete sa permission-test-sa
          # As cluster admin, should succeed
          CLUSTER_ADMIN_TOKEN=$(oc whoami -t)
          oc rsh ${POD} curl http://localhost:${PORT}/metrics -H "Authorization: Bearer $CLUSTER_ADMIN_TOKEN" -k
        remediation: |
          A fix to this issue: https://bugzilla.redhat.com/show_bug.cgi?id=1889488 None required.
          Profiling is protected by RBAC and cannot be disabled.
        scored: false

      - id: 1.4.2
        text: "Verify that the scheduler API service is protected by RBAC (Manual)"
        type: manual
        audit: |
          echo "Describing kube-scheduler endpoints..."
          oc -n openshift-kube-scheduler describe endpoints

          echo "Checking pod configuration for kube-scheduler to confirm no --bind-address or insecure arguments..."
          oc -n openshift-kube-scheduler get cm kube-scheduler-pod -o json \
            | jq -r '.data["pod.yaml"]' \
            | jq '.spec.containers[] | select(.name=="kube-scheduler") | .args'

          echo "Testing access to metrics endpoint as unauthenticated user..."
          oc project openshift-kube-scheduler
          export POD=$(oc get pods -l app=openshift-kube-scheduler -o jsonpath='{.items[0].metadata.name}')
          export POD_IP=$(oc get pods -l app=openshift-kube-scheduler -o jsonpath='{.items[0].status.podIP}')
          export PORT=$(oc get pod $POD -o jsonpath='{.spec.containers[0].livenessProbe.httpGet.port}')
          oc rsh $POD curl -k -s -o /dev/null -w "%{http_code}" https://$POD_IP:$PORT/metrics

          echo "Testing access with unprivileged service account..."
          oc create sa permission-test-sa
          export SA_TOKEN=$(oc create token permission-test-sa)
          oc rsh $POD curl -k -s -o /dev/null -w "%{http_code}" https://$POD_IP:$PORT/metrics -H "Authorization: Bearer $SA_TOKEN"

          echo "Testing access with cluster-admin..."
          export CLUSTER_ADMIN_TOKEN=$(oc whoami -t)
          oc rsh $POD curl -k -s -o /dev/null -w "%{http_code}" https://$POD_IP:$PORT/metrics -H "Authorization: Bearer $CLUSTER_ADMIN_TOKEN"

          # Cleanup
          unset CLUSTER_ADMIN_TOKEN POD PORT SA_TOKEN POD_IP
          oc delete sa permission-test-sa
        remediation: |
          By default, the --bind-address argument is not present,
          the readinessProbe and livenessProbe arguments are set to 10251 and the port argument is set to 0.
          Check the status of this issue: https://bugzilla.redhat.com/show_bug.cgi?id=1889488
        scored: false
