From fb526d2a208490c9fd81aa15446abeb76c11e6f8 Mon Sep 17 00:00:00 2001 From: Derek Nola Date: Mon, 22 Apr 2024 17:00:00 -0700 Subject: [PATCH] Convert to kube-bench json output Signed-off-by: Derek Nola --- scripts/kubebench-to-markdown.sh | 167 ++++++++++--------------------- 1 file changed, 54 insertions(+), 113 deletions(-) diff --git a/scripts/kubebench-to-markdown.sh b/scripts/kubebench-to-markdown.sh index 0c0264f7c..42d3c3184 100755 --- a/scripts/kubebench-to-markdown.sh +++ b/scripts/kubebench-to-markdown.sh @@ -1,129 +1,70 @@ #!/bin/bash -# Define the YAML file -yaml_file="$1" -yaml_temp="$yaml_file.tmp" -cp "$yaml_file" "$yaml_temp" +# To generate the expected json report, run the following command: +# kube-bench --benchmark=k3s-cis-1.7 --json > k3s-cis-1.7.json +# Then pass the json file to this script: +# ./kubebench-to-markdown.sh k3s-cis-1.7.json -handle_op_checks () { - test=$1 - flag=$(echo "$test" | jq -r '.flag') - expected="" - if [ "$flag" == "permissions" ]; then - value=$(echo "$test" | jq -r '.compare.value') - expected="file permissions are \`$value\`" - elif [ "$flag" == "700" ]; then - value=$(echo "$test" | jq -r '.compare.value') - expected="directory permissions are \`$value\`" - elif [ "$flag" == "root:root" ]; then - expected="file owners are \`root:root\`" - elif [ "$(echo "$test" | jq -r '.compare')" != "null" ]; then - op=$(echo "$test" | jq -r '.compare.op') - value=$(echo "$test" | jq -r '.compare.value') - case $op in - eq) op="equals" ;; - noteq) op="not equal" ;; - nothave) op="does not have" ;; - gte) op="greater than" ;; - null) op="" ;; - esac - case $value in - null) value="" ;; - esac - expected="$flag $op $value" - else - set=$(echo "$test" | jq -r '.set') - if [ "$set" == "true" ]; then - expected="$flag is set" - elif [ "$set" == "false" ]; then - expected="$flag is not set" - else - expected="$flag is found" - fi - fi -} -# Replace kubebench variables with actual values -sed -i 's/\$apiserverconf/\/etc\/kubernetes\/manifests\/kube-apiserver.yaml/g' "$yaml_temp" -sed -i 's/\$schedulerconf/\/etc\/kubernetes\/manifests\/kube-scheduler.yaml/g' "$yaml_temp" -sed -i 's/\$controllermanagerconf/\/etc\/kubernetes\/manifests\/kube-controller-manager.yaml/g' "$yaml_temp" -sed -i 's/\$controllermanagerkubeconfig/\/var\/lib\/rancher\/k3s\/server\/cred\/controller.kubeconfig/g' "$yaml_temp" -sed -i 's/\$apiserverbin/containerd/g' "$yaml_temp" -sed -i 's/\$etcdbin/containerd/g' "$yaml_temp" -sed -i 's/\$kubeletbin/containerd/g' "$yaml_temp" -sed -i 's/\$controllermanagerbin/containerd/g' "$yaml_temp" +# Remove 3.X checks, as k3s doesn't do them +clean=$(jq 'del(.Controls[2])' "$1") -# Read all checks entries, ignore high-level groups -yq e '.groups[].checks | flatten' -o=json "$yaml_temp" | jq -c '.[]' | while read -r check; do - # Extract fields from each check - id=$(echo "$check" | jq -r '.id') - text=$(echo "$check" | jq -r '.text') - type=$(echo "$check" | jq -r '.type') - audit=$(echo "$check" | jq -r '.audit') - scored=$(echo "$check" | jq -r '.scored') - remediation=$(echo "$check" | jq -r '.remediation') - - # Print the ID as a Markdown header - echo "### $id $text" +# Print section titles, needs to be moved by hand to the right place +sections=$(echo "$clean" | jq -c '.Controls[].tests[]') +echo "$sections" | while read -r section; do + id=$(echo "$section" | jq -r '.section') + description=$(echo "$section" | jq -r '.desc') + echo "## $id $description" echo +done - # Encase lines starting with "chown" in backticks - remediation=$(echo "$remediation" | sed '/^\s*\(chmod\|chown\)/s/.*/`&`/') - - # Check if remediation is "Not Applicable." - if [[ "$remediation" == *"Not Applicable."* ]]; then - # Remove "Not Applicable." from the remediation text - remediation=$(echo "$remediation" | sed '/Not Applicable./d') - - echo "**Result:** Not Applicable" - echo - fi - - # if scored is true and type doesn't exist, print "Result: Passed" - if [ "$scored" == "true" ] && [ "$type" == "null" ]; then - tests=$(echo "$check" | jq -r '.tests.test_items') - bin_op=$(echo "$check" | jq -r '.tests.bin_op') - - echo "**Result:** Passed" - echo - echo "**Audit:**" - echo "\`\`\`bash" - echo "$audit" - echo "\`\`\`" - echo +# Read all result entries, ignore high-level groups +echo "$clean" | jq -c '.Controls[].tests[].results[]' | while read -r result; do + + # Output details in markdown format + status=$(echo "$result" | jq -r '.status') + id=$(echo "$result" | jq -r '.test_number') + title=$(echo "$result" | jq -r '.test_desc') + audit=$(echo "$result" | jq -r '.audit') + expected_result=$(echo "$result" | jq -r '.expected_result') + actual_value=$(echo "$result" | jq -r '.actual_value') + remediation=$(echo "$result" | jq -r '.remediation') + echo "### $id $title" + echo - if [ "$(echo "$tests" | jq -r 'length')" -eq 1 ]; then - handle_op_checks "$(echo "$tests" | jq -r '.[0]')" - echo "**Expected Result:** $expected" - echo - echo "**Returned Value:**" - echo "\`\`\`console" + case $status in + PASS | FAIL) + echo "**Result:** $status" echo + echo "**Audit:**" + echo "\`\`\`bash" + echo "$audit" echo "\`\`\`" echo - elif [ "$bin_op" == "and" ] || [ "$bin_op" == "or" ]; then - handle_op_checks "$(echo "$tests" | jq -r '.[0]')" - echo "**Expected Result:**" - echo "$expected" - echo "$bin_op" - handle_op_checks "$(echo "$tests" | jq -r '.[1]')" - echo "$expected" + echo "**Expected Result:** $expected_result" + echo + echo "
Returned Value:" echo - echo "**Returned Value:**" echo "\`\`\`console" - echo + echo "$actual_value" echo "\`\`\`" + echo "
" echo - fi - fi - - # Print remediation if not empty - if [ -n "$remediation" ]; then - echo "**Remediation:**" - echo "$remediation" - echo - fi -done - -rm "$yaml_temp" \ No newline at end of file + ;; + WARN) + echo "**Result:** $status" + echo + echo "**Remediation:**" + echo "$remediation" + echo + ;; + INFO) + echo "**Result:** Not Applicable" + echo + echo "**Remediation:**" + echo "$remediation" + echo + ;; + esac +done \ No newline at end of file