Переглянути джерело

added fix policy stuff. in bash. har har, ansible.

Grega Bremec 3 роки тому
батько
коміт
ec2e7baf4f

+ 11 - 0
fix-policies.yml

@@ -0,0 +1,11 @@
+---
+- name: Fix the offending noisy policies with exclusion scopes
+  hosts: workstation.lab.example.com
+  gather_subset: min
+  become: no
+  roles:
+#    - role: check-env
+#      tags: check
+    - role: fix-policies
+      tags: configure
+...

+ 16 - 0
roles/fix-policies/files/dump-policies.sh

@@ -0,0 +1,16 @@
+#!/bin/bash
+#
+# Create policy database backup.
+#
+# Need files:
+#   api-token (api auth token)
+#
+E=central-rhacs.apps.ocp4.example.com
+T=$(cat roxctl-token)
+
+# Get a list of policy IDs
+IDS="$(curl -ks -H "Authorization: Bearer ${T}" https://${E}/v1/policies | jq -r '[ .policies[].id ]')"
+
+# Create a dump
+curl -ks -H "Authorization: Bearer ${T}" -XPOST -d "{ \"policyIds\": ${IDS} }" https://central-rhacs.apps.ocp4.example.com/v1/policies/export
+

+ 172 - 0
roles/fix-policies/files/fix-policies.sh

@@ -0,0 +1,172 @@
+#!/bin/bash
+#
+# Generate modified policies. Patch existing modified policies to disable them.
+#
+# Need files:
+#   policyexport (a dump of all policies)
+#   roxctl-token (api auth token)
+#
+E=central-rhacs.apps.ocp4.example.com
+T=$(cat roxctl-token)
+
+DISABLED_POLICY_LIST=(
+	"ADD Command used instead of COPY"
+	"Container using read-write root filesystem"
+	"Curl in Image"
+	"Docker CIS 4.4: Ensure images are scanned and rebuilt to include security patches"
+	"Docker CIS 5.21: Ensure the default seccomp profile is not disabled"
+	"Fixable CVSS >= 7"
+	"Images with no scans"
+	"Login Binaries"
+	"Password Binaries"
+	"Process with UID 0"
+	"Required Annotation: Email"
+	"Required Annotation: Owner/Team"
+	"Required Image Label"
+	"Required Label: Owner/Team"
+	"Secret Mounted as Environment Variable"
+	"SetUID Processes"
+	"Shadow File Modification"
+	"Shell Management"
+	"Wget in Image"
+    )
+
+ENABLED_POLICY_LIST=(
+	"30-Day Scan Age"
+	"Alpine Linux Package Manager Execution"
+	"Apache Struts: CVE-2017-5638"
+	"CAP_SYS_ADMIN capability added"
+	"Compiler Tool Execution"
+	"Cryptocurrency Mining Process Execution"
+	"Docker CIS 4.7: Alert on Update Instruction"
+	"Docker CIS 5.1 Ensure that, if applicable, an AppArmor Profile is enabled"
+	"Docker CIS 5.19: Ensure mount propagation mode is not enabled"
+	"Emergency Deployment Annotation"
+	"Improper Usage of Orchestrator Secrets Volume"
+	"Insecure specified in CMD"
+	"Iptables Executed in Privileged Container"
+	"Kubernetes Actions: Exec into Pod"
+	"Kubernetes Actions: Port Forward to Pod"
+	"Kubernetes Dashboard Deployed"
+	"Latest tag"
+	"Linux Group Add Execution"
+	"Linux User Add Execution"
+	"Log4Shell: log4j Remote Code Execution vulnerability"
+	"Netcat Execution Detected"
+	"Network Management Execution"
+	"OpenShift: Advanced Cluster Security Central Admin Secret Accessed"
+	"OpenShift: Kubeadmin Secret Accessed"
+	"OpenShift: Kubernetes Secret Accessed by an Impersonated User"
+	"Process Targeting Cluster Kubelet Endpoint"
+	"Process Targeting Cluster Kubernetes Docker Stats Endpoint"
+	"Process Targeting Kubernetes Service Endpoint"
+	"Red Hat Package Manager Execution"
+	"Remote File Copy Binary Execution"
+	"Secure Shell (ssh) Port Exposed"
+	"Secure Shell (ssh) Port Exposed in Image"
+	"Secure Shell Server (sshd) Execution"
+	"Shell Spawned by Java Application"
+	"Ubuntu Package Manager Execution"
+	"Ubuntu Package Manager in Image"
+	"Unauthorized Network Flow"
+	"Unauthorized Process Execution"
+	"chkconfig Execution"
+	"crontab Execution"
+	"iptables Execution"
+	"nmap Execution"
+	"systemctl Execution"
+	"systemd Execution"
+    )
+
+FIX_POLICY_LIST=(
+	"90-Day Image Age"
+	"Alpine Linux Package Manager (apk) in Image"
+	"Docker CIS 4.1: Ensure That a User for the Container Has Been Created"
+	"Docker CIS 5.15: Ensure that the host's process namespace is not shared"
+	"Docker CIS 5.16: Ensure that the host's IPC namespace is not shared"
+	"Docker CIS 5.7: Ensure privileged ports are not mapped within containers"
+	"Docker CIS 5.9 and 5.20: Ensure that the host's network namespace is not shared"
+	"Environment Variable Contains Secret"
+	"Fixable CVSS >= 6 and Privileged"
+	"Fixable Severity at least Important"
+	"Mount Container Runtime Socket"
+	"Mounting Sensitive Host Directories"
+	"No resource requests or limits specified"
+	"Pod Service Account Token Automatically Mounted"	
+	"Privileged Container"
+	"Red Hat Package Manager in Image"
+    )
+
+echo "Fixed policies: ${#FIX_POLICY_LIST[*]}"
+echo "Enabled policies: ${#ENABLED_POLICY_LIST[*]}"
+echo "Disabled policies: ${#DISABLED_POLICY_LIST[*]}"
+
+# Export current policies.
+# TODO: decide when to use backup and when to use current state
+# XXX: can't delete system policies anyway
+
+oldIFS="${IFS}"
+newIFS='
+'
+IFS="${newIFS}"
+for p in ${DISABLED_POLICY_LIST[*]}; do
+    id="$(jq -r '.policies[] | select(.name == "'${p}'") | .id' < policyexport)"
+    if [ -z "${id}" ]; then
+	echo "ERROR: Could not look up ID of \"${p}\"!"
+	continue
+    fi
+    echo -n "Disabling \"${p}\" ($id)... "
+    curl -ks -XPATCH -H "Authorization: Bearer ${T}" -d '{"id": "'${id}'", "disabled": true}' https://${E}/v1/policies/${id}
+    echo
+done
+
+for p in ${ENABLED_POLICY_LIST[*]}; do
+    id="$(jq -r '.policies[] | select(.name == "'${p}'") | .id' < policyexport)"
+    if [ -z "${id}" ]; then
+	echo "ERROR: Could not look up ID of \"${p}\"!"
+	continue
+    fi
+    echo -n "Enabling \"${p}\" ($id)... "
+    curl -ks -XPATCH -H "Authorization: Bearer ${T}" -d '{"id": "'${id}'", "disabled": false}' https://${E}/v1/policies/${id}
+    echo
+done
+
+for p in ${FIX_POLICY_LIST[*]}; do
+    # find matching policies
+    ids="$(jq -r '.policies[] | select(.name | test("(?i)^'${p//[()]/.}'")) | .id' < policyexport)"
+    if [ -z "${ids}" ]; then
+	echo "ERROR: Could not look up ID(s) of \"${p//[()]/.}\"!"
+	continue
+    fi
+    # how many?
+    nmatch=$(echo "${ids}" | wc -l | tr -d '[[:space:]]')
+    echo "Found ${nmatch} policies matching \"${p}\"..."
+    if [ ${nmatch} -gt 1 ]; then
+	# delete the one(s) that are not an exact match
+	id="$(jq -r '.policies[] | select(.name == "'${p}'") | .id' < policyexport)"
+	if [ -z "${id}" ]; then
+	    echo "ERROR: Could not find exact match for \"${p}\"!"
+	    continue
+	fi
+	xtraids="$(echo "${ids}" | grep -v ${id})"
+	for rmid in ${xtraids}; do
+	    echo -n "  - removing: ${rmid}"
+	    curl -ks -XDELETE -H "Authorization: Bearer ${T}" https://${E}/v1/policies/${rmid}
+	    echo
+	done
+    else
+	id="${ids}"
+    fi
+
+    echo "  - keeping: ${id}"
+    echo -n "      disabling... "
+    curl -ks -XPATCH -H "Authorization: Bearer ${T}" -d '{"id": "'${id}'", "disabled": true}' https://${E}/v1/policies/${id}
+    echo
+    echo -n "      creating patched copy... "
+    PAYLOAD="$(jq '.policies[] | select(.id == "'${id}'") | .exclusions |= [ { "name": "Skip system namespaces", "deployment": { "name": "", "scope": { "cluster": "", "namespace": "^kube-.*|^openshift-.*|^istio-.*|^rhacs$|^stackrox$", "label": null } }, "image": null, "expiration": null } ] | .name |= . + " (non-system)" | .id |= "" | .disabled |= false' < policyexport)"
+    curl -ks -XPOST -H "Authorization: Bearer ${T}" -d "${PAYLOAD}" https://${E}/v1/policies
+    echo
+done
+IFS="${oldIFS}"
+unset oldIFS newIFS
+

+ 101 - 0
roles/fix-policies/tasks/main.yml

@@ -0,0 +1,101 @@
+---
+- name: Check for presence of roxctl-token
+  stat:
+    path: "{{ ansible_facts['user_dir'] }}/roxctl-token"
+  register: user_token_file
+
+- name: Alternatively, fall back to api-token
+  stat:
+    path: "{{ ansible_facts['user_dir'] }}/api-token"
+  register: auto_token_file
+
+- assert:
+    that: user_token_file.stat.exists or auto_token_file.stat.exists
+    fail_msg: "ERROR: No roxctl-token file found."
+    success_msg: "OK, proceeding with token from roxctl-token."
+
+- name: Symlink api-token to roxctl-token if latter is missing
+  file:
+    path: "{{ ansible_facts['user_dir'] }}/roxctl-token"
+    src: "{{ ansible_facts['user_dir'] }}/api-token"
+    state: link
+  when: not user_token_file.stat.exists
+  register: symlink_token
+
+- name: Ensure the scripts/ directory is there
+  file:
+    path: "{{ ansible_facts['user_dir'] }}/scripts"
+    state: directory
+    owner: "{{ ansible_user }}"
+    group: "{{ ansible_user }}"
+    mode: 0755
+
+- name: Ensure the two scripts are there
+  copy:
+    src: files/{{ item }}
+    dest: "{{ ansible_facts['user_dir'] }}/scripts/{{ item }}"
+    owner: "{{ ansible_user }}"
+    group: "{{ ansible_user }}"
+    mode: 0755
+  loop:
+    - dump-policies.sh
+    - fix-policies.sh
+
+- name: Does policyexport exist?
+  stat:
+    path: "{{ ansible_facts['user_dir'] }}/policyexport"
+  register: policy_export
+
+- name: Does api-policies exist?
+  stat:
+    path: "{{ ansible_facts['user_dir'] }}/api-policies"
+  register: policy_backup
+
+- name: Fall back to api-policies if one exists
+  file:
+    path: "{{ ansible_facts['user_dir'] }}/policyexport"
+    src: "{{ ansible_facts['user_dir'] }}/api-policies"
+    state: link
+  when:
+    - not policy_export.stat.exists
+    - policy_backup.stat.exists
+  register: symlink_policies
+
+- name: Dump the policies
+  ansible.builtin.shell:
+    chdir: "{{ ansible_facts['user_dir'] }}"
+    cmd: ./scripts/dump-policies.sh > policyexport
+    creates: "{{ ansible_facts['user_dir'] }}/policyexport"
+
+- name: Fix the policies
+  ansible.builtin.command:
+    chdir: "{{ ansible_facts['user_dir'] }}"
+    cmd: ./scripts/fix-policies.sh
+
+- name: Clean up token symlink
+  file:
+    path: "{{ ansible_facts['user_dir'] }}/roxctl-token"
+    state: absent
+  when: symlink_token is defined
+
+- name: Clean up policy symlink
+  file:
+    path: "{{ ansible_facts['user_dir'] }}/policyexport"
+    state: absent
+  when: symlink_policies is defined
+
+# Get a list of policies:
+# curl -ks -H "Authorization: Bearer $(cat roxctl-token)" -XGET https://central-rhacs.apps.ocp4.example.com/v1/policies | jq -r '[ .policies[].id ]'
+#
+# Dump the policies in that list:
+# curl -ks -H "Authorization: Bearer $(cat roxctl-token)" -XPOST -d "{ \"policyIds\": $(cat policyids) }" https://central-rhacs.apps.ocp4.example.com/v1/policies/export
+#
+# Match a policy by name and print its ID:
+# jq '.policies[] | select(.name | test("(?i)docker cis 4\\.1")) | .id'
+#
+# Fix a policy and return the fixed list:
+# jq '.policies |= map(if .name | test("(?i)docker cis 4\\.1") then .exclusions = [ { "name": "Skip system namespaces", "deployment": { "name": "", "scope": { "cluster": "", "namespace": "^kube-.*|^openshift-.*|^istio-.*|^rhacs$|^stackrox$", "label": null } }, "image": null, "expiration": null } ] else . end)'
+#
+# Extract one policy and fix it (along with its name):
+# jq '.policies[] | select(.name | test("(?i)docker cis 4\\.1")) | .exclusions |= [ { "name": "Skip system namespaces", "deployment": { "name": "", "scope": { "cluster": "", "namespace": "^kube-.*|^openshift-.*|^istio-.*|^rhacs$|^stackrox$", "label": null } }, "image": null, "expiration": null } ] | .name |= . + " (non-system)"'
+...