使用 Ansible 批量回收非白名单 sudo 权限(AWK 精确匹配)

使用 Ansible 批量回收非白名单 sudo 权限(AWK 精确匹配)

 次点击
16 分钟阅读

在企业运维场景中,经常需要统一清理非授权用户的 sudo 权限
尤其是当多个账号在 /etc/sudoers/etc/sudoers.d/* 中残留提权配置时,手动检查和修改既繁琐又危险。

本文介绍一种 安全、可回滚、可批量执行的自动化方案 ——
通过 Ansible + awk 列匹配,实现精准回收非白名单用户的 sudo 权限。


✅ 设计思路

  1. 按列匹配 sudo 授权格式

    • 标准授权行一般为:

      user ALL=(root) NOPASSWD:ALL
    • awk 按列识别 $1(用户名)、$2(ALL=(root))、$3(NOPASSWD:ALL);

    • 仅当这三列完全匹配时才执行注释操作,避免误伤其他配置。

  2. 保留白名单用户

    • 白名单用户可通过变量 whitelist 定义;

    • 所有在白名单中的用户权限保留。

  3. 自动备份与回滚

    • 修改前自动生成备份文件:
      /etc/sudoers.bak_YYYYMMDDHHMMSS

    • 任何校验失败自动回滚。

  4. 语法安全校验

    • 每次修改后使用 visudo -c 验证文件语法;

    • 确保不会因配置错误导致 sudo 不可用。


🧩 完整 Playbook

文件名推荐:revoke_sudo.yml

---
- name: 批量回收非白名单 sudo 权限(awk 列匹配)
  hosts: all
  become: yes
  gather_facts: yes

  vars:
    # ✅ 定义白名单(这些用户保留 sudo 权限)
    whitelist: [admin, opsuser, dbmaint, system, monitor]
    files_to_fix:
      - /etc/sudoers

  tasks:
    - name: 逐个文件处理(备份 → awk 注释 → 校验 → 覆盖)
      block:
        - name: 备份 sudoers 文件
          ansible.builtin.shell: |
            cp -a "{{ item }}" "{{ item }}.bak_$(date +%Y%m%d%H%M%S)"
          args:
            executable: /bin/bash
          loop: "{{ files_to_fix }}"

        - name: 使用 awk 严格列匹配注释非白名单 sudo 行
          ansible.builtin.shell: |
            set -euo pipefail
            tmp="$(mktemp)"
            awk -v WL="{{ whitelist | join(" ") }}" '
              BEGIN{
                split(WL, wla, " ");
                for (i in wla) wl[wla[i]] = 1;
              }
              /^[[:space:]]*#/ { print; next }

              {
                user=$1
                if (($2=="ALL=(root)" || $2=="ALL=(ALL)") && $3=="NOPASSWD:ALL" && !(user in wl)) {
                  print "#" $0
                } else {
                  print
                }
              }
            ' "{{ item }}" > "$tmp"

            visudo -c -f "$tmp" >/dev/null && install -o root -g root -m 0440 "$tmp" "{{ item }}"
            rm -f "$tmp"
          args:
            executable: /bin/bash
          loop: "{{ files_to_fix }}"
          loop_control:
            label: "{{ item }}"

      rescue:
        - name: 回滚到最近备份(如校验失败)
          ansible.builtin.shell: |
            set -e
            f="{{ item }}"
            b="$(ls -1t ${f}.bak_* 2>/dev/null | head -n1 || true)"
            if [ -n "$b" ]; then
              install -o root -g root -m 0440 "$b" "$f"
            fi
          args:
            executable: /bin/bash
          loop: "{{ files_to_fix }}"
          loop_control:
            label: "{{ item }}"

        - name: 失败提示
          ansible.builtin.fail:
            msg: "visudo 校验失败,已回滚最近备份,请检查 sudoers 内容与匹配逻辑。"

⚙️ 执行方法

ansible-playbook revoke_sudo.yml -i hosts

执行后会:

  1. 自动备份原文件;

  2. 过滤非白名单用户的全权限 sudo 行;

  3. 添加注释符号 #

  4. 校验语法;

  5. 校验失败时自动回滚。


🧠 逻辑验证示例

假设 /etc/sudoers 内容如下:

# 保留的用户
admin ALL=(root) NOPASSWD:ALL
dbmaint ALL=(root) NOPASSWD:ALL

# 应该被回收的
testuser ALL=(root) NOPASSWD:ALL
legacyuser ALL=(ALL) NOPASSWD:ALL

执行后结果:

admin ALL=(root) NOPASSWD:ALL
dbmaint ALL=(root) NOPASSWD:ALL
#testuser ALL=(root) NOPASSWD:ALL
#legacyuser ALL=(ALL) NOPASSWD:ALL

✅ 总结

功能

说明

安全

修改前自动备份、失败回滚

精确

按列匹配,不受空格/Tab影响

可控

支持白名单自定义

可批量

适合企业大规模主机批量执行


💡 扩展建议
如果你的环境中还存在 /etc/sudoers.d/ 子文件,可以在 vars 里添加参数:

files_to_fix:
  - /etc/sudoers
  - /etc/sudoers.d/appadmin

这样同样支持多文件批量处理。

© 本文著作权归作者所有,未经许可不得转载使用。