Skip to content

Catch NOPASSWD and !authenticate without a leading space in sudo rules - #15202

Open
yadlapatisathwik wants to merge 1 commit into
ComplianceAsCode:masterfrom
yadlapatisathwik:fix-sudo-nopasswd-no-space
Open

yadlapatisathwik wants to merge 1 commit into
ComplianceAsCode:masterfrom
yadlapatisathwik:fix-sudo-nopasswd-no-space

Conversation

@yadlapatisathwik

Copy link
Copy Markdown
Contributor

Description:

  • sudo_remove_nopasswd and sudo_remove_no_authenticate OVAL: the tag may follow whitespace or one of ,=): (NOPASSWD) or , (!authenticate). Before, only whitespace was accepted.
  • bash_sudo_remove_config and ansible_sudo_remove_config: use the same prefix, so the remediations comment out the same lines the check reports.
  • bash_sudo_remove_config: comment out matching lines by line number instead of using the line text as a sed regex.
  • New test scenarios: nopasswd_no_runas_no_space.fail.sh, nopasswd_after_runas_no_space.fail.sh, nopasswd_after_other_tag.fail.sh, nopasswd_wildcard_command.fail.sh and no_authenticate_after_comma.fail.sh.

Rationale:

  • sudoers does not need a space before a tag or a Defaults option. %wheel ALL=NOPASSWD: ALL, %wheel ALL=(ALL)NOPASSWD:ALL, %wheel ALL=(ALL) SETENV:NOPASSWD: ALL and Defaults:%wheel env_reset,!authenticate are all valid and all let wheel members use sudo without a password, but the rules reported pass and the remediations left the lines in place.

  • The bash macro used each matched line as a regex, so a line with * (for example NOPASSWD: /usr/bin/systemctl restart *) was never commented out.

  • These rules back RHEL-08-010380, RHEL-08-010381, RHEL-09-611085, RHEL-09-432025, RHEL-10-600530, RHEL-10-600560 and CIS 5.2.4 / 5.2.5.

  • Fixes sudo_remove_nopasswd and sudo_remove_no_authenticate pass when there is no space before NOPASSWD or !authenticate #15201

Review Hints:

  • The new .fail.sh scenarios pass on current master before remediation (the false pass), and with this change they fail before remediation and pass after it, for both bash and Ansible. On a Rocky Linux 9 test system a wheel member could run sudo -n before the change and gets "a password is required" after it.
  • Run them with: ./tests/automatus.py rule --libvirt qemu:///system rhel9 --datastream build/ssg-rhel9-ds.xml sudo_remove_nopasswd sudo_remove_no_authenticate (and --remediate-using ansible).
  • sudo_require_authentication extends both checks and uses the same macros, so it picks up the change.
  • sudo_vdsm_nopasswd (RHV) has the same pattern and is left as it is.

@openshift-ci

openshift-ci Bot commented Oct 15, 2026

Copy link
Copy Markdown

Hi @yadlapatisathwik. Thanks for your PR.

I'm waiting for a ComplianceAsCode member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work.

Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@github-actions

Copy link
Copy Markdown
Contributor

This datastream diff is auto generated by the check Compare DS/Generate Diff

Click here to see the full diff
bash remediation for rule 'xccdf_org.ssgproject.content_rule_sudo_remove_no_authenticate' differs.
--- xccdf_org.ssgproject.content_rule_sudo_remove_no_authenticate
+++ xccdf_org.ssgproject.content_rule_sudo_remove_no_authenticate
@@ -5,12 +5,12 @@
   if [ ! -e "$f" ] ; then
     continue
   fi
-  matching_list=$(grep -P '^(?!#).*[\s]+\!authenticate.*$' $f | uniq )
-  if ! test -z "$matching_list"; then
-    while IFS= read -r entry; do
+  matching_lines=$(grep -nP '^(?!#).*[\s,=):]\!authenticate.*$' "$f" | cut -d: -f1)
+  if ! test -z "$matching_lines"; then
+    for line_number in $matching_lines; do
       # comment out "!authenticate" matches to preserve user data
-      sed -i "s|^${entry}$|# &|g" $f
-    done <<< "$matching_list"
+      sed -i "${line_number}s/^/# /" "$f"
+    done
 
     /usr/sbin/visudo -cf $f &> /dev/null || echo "Fail to validate $f with visudo"
   fi

ansible remediation for rule 'xccdf_org.ssgproject.content_rule_sudo_remove_no_authenticate' differs.
--- xccdf_org.ssgproject.content_rule_sudo_remove_no_authenticate
+++ xccdf_org.ssgproject.content_rule_sudo_remove_no_authenticate
@@ -35,7 +35,7 @@
 
 - name: Remove lines containing !authenticate from sudoers files
   ansible.builtin.replace:
-    regexp: (^(?!#).*[\s]+\!authenticate.*$)
+    regexp: (^(?!#).*[\s,=):]\!authenticate.*$)
     replace: '# \g<1>'
     path: '{{ item.path }}'
     validate: /usr/sbin/visudo -cf %s

bash remediation for rule 'xccdf_org.ssgproject.content_rule_sudo_remove_nopasswd' differs.
--- xccdf_org.ssgproject.content_rule_sudo_remove_nopasswd
+++ xccdf_org.ssgproject.content_rule_sudo_remove_nopasswd
@@ -5,12 +5,12 @@
   if [ ! -e "$f" ] ; then
     continue
   fi
-  matching_list=$(grep -P '^(?!#).*[\s]+NOPASSWD[\s]*\:.*$' $f | uniq )
-  if ! test -z "$matching_list"; then
-    while IFS= read -r entry; do
+  matching_lines=$(grep -nP '^(?!#).*[\s,=):]NOPASSWD[\s]*\:.*$' "$f" | cut -d: -f1)
+  if ! test -z "$matching_lines"; then
+    for line_number in $matching_lines; do
       # comment out "NOPASSWD" matches to preserve user data
-      sed -i "s|^${entry}$|# &|g" $f
-    done <<< "$matching_list"
+      sed -i "${line_number}s/^/# /" "$f"
+    done
 
     /usr/sbin/visudo -cf $f &> /dev/null || echo "Fail to validate $f with visudo"
   fi

ansible remediation for rule 'xccdf_org.ssgproject.content_rule_sudo_remove_nopasswd' differs.
--- xccdf_org.ssgproject.content_rule_sudo_remove_nopasswd
+++ xccdf_org.ssgproject.content_rule_sudo_remove_nopasswd
@@ -35,7 +35,7 @@
 
 - name: Remove lines containing NOPASSWD from sudoers files
   ansible.builtin.replace:
-    regexp: (^(?!#).*[\s]+NOPASSWD[\s]*\:.*$)
+    regexp: (^(?!#).*[\s,=):]NOPASSWD[\s]*\:.*$)
     replace: '# \g<1>'
     path: '{{ item.path }}'
     validate: /usr/sbin/visudo -cf %s

bash remediation for rule 'xccdf_org.ssgproject.content_rule_sudo_require_authentication' differs.
--- xccdf_org.ssgproject.content_rule_sudo_require_authentication
+++ xccdf_org.ssgproject.content_rule_sudo_require_authentication
@@ -5,12 +5,12 @@
   if [ ! -e "$f" ] ; then
     continue
   fi
-  matching_list=$(grep -P '^(?!#).*[\s]+NOPASSWD[\s]*\:.*$' $f | uniq )
-  if ! test -z "$matching_list"; then
-    while IFS= read -r entry; do
+  matching_lines=$(grep -nP '^(?!#).*[\s,=):]NOPASSWD[\s]*\:.*$' "$f" | cut -d: -f1)
+  if ! test -z "$matching_lines"; then
+    for line_number in $matching_lines; do
       # comment out "NOPASSWD" matches to preserve user data
-      sed -i "s|^${entry}$|# &|g" $f
-    done <<< "$matching_list"
+      sed -i "${line_number}s/^/# /" "$f"
+    done
 
     /usr/sbin/visudo -cf $f &> /dev/null || echo "Fail to validate $f with visudo"
   fi
@@ -20,12 +20,12 @@
   if [ ! -e "$f" ] ; then
     continue
   fi
-  matching_list=$(grep -P '^(?!#).*[\s]+\!authenticate.*$' $f | uniq )
-  if ! test -z "$matching_list"; then
-    while IFS= read -r entry; do
+  matching_lines=$(grep -nP '^(?!#).*[\s,=):]\!authenticate.*$' "$f" | cut -d: -f1)
+  if ! test -z "$matching_lines"; then
+    for line_number in $matching_lines; do
       # comment out "!authenticate" matches to preserve user data
-      sed -i "s|^${entry}$|# &|g" $f
-    done <<< "$matching_list"
+      sed -i "${line_number}s/^/# /" "$f"
+    done
 
     /usr/sbin/visudo -cf $f &> /dev/null || echo "Fail to validate $f with visudo"
   fi

ansible remediation for rule 'xccdf_org.ssgproject.content_rule_sudo_require_authentication' differs.
--- xccdf_org.ssgproject.content_rule_sudo_require_authentication
+++ xccdf_org.ssgproject.content_rule_sudo_require_authentication
@@ -35,7 +35,7 @@
 
 - name: Remove lines containing NOPASSWD from sudoers files
   ansible.builtin.replace:
-    regexp: (^(?!#).*[\s]+NOPASSWD[\s]*\:.*$)
+    regexp: (^(?!#).*[\s,=):]NOPASSWD[\s]*\:.*$)
     replace: '# \g<1>'
     path: '{{ item.path }}'
     validate: /usr/sbin/visudo -cf %s
@@ -77,7 +77,7 @@
 
 - name: Remove lines containing !authenticate from sudoers files
   ansible.builtin.replace:
-    regexp: (^(?!#).*[\s]+\!authenticate.*$)
+    regexp: (^(?!#).*[\s,=):]\!authenticate.*$)
     replace: '# \g<1>'
     path: '{{ item.path }}'
     validate: /usr/sbin/visudo -cf %s

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-ok-to-test Used by openshift-ci bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sudo_remove_nopasswd and sudo_remove_no_authenticate pass when there is no space before NOPASSWD or !authenticate

1 participant