Linux

使用 Ansible 檢查 sudoers.d 文件

  • April 1, 2021

我有一個 Ansible 劇本,用於在我們的環境中管理我們的 sudoers 文件。我們喜歡在 /etc/sudoers 中保留一個最小的 sudoers 文件,然後我們想要添加的任何內容都放在 /etc/sudoers.d 下的單獨文件中。

我的 Ansible playbook 包含以下用於推送這些文件的任務:

- name: copy sudoers files
 copy:
   src: "{{ item }}"
   dest: "/etc/sudoers.d/{{ item }}"
   backup: yes
   owner: root
   group: root
   mode: 0440
   validate: /usr/sbin/visudo -cf %s
 with_items:
   - admins
   - apache
   - monitor

該任務包含一個 validate 子句,用於在送出文件之前確保文件有效,這通常執行良好。但是,今天我遇到了一個更新破壞 sudo 的問題。該文件通過了驗證步驟,但包含一個與主 /etc/sudoers 文件中的 User_Alias 同名的 User_Alias。此後任何嘗試執行 sudo 都會導致解析錯誤。

我的問題是——如何測試來自 Ansible 的 sudoers 文件的更新,以擷取這樣的錯誤?文件到位後,可以通過執行來擷取錯誤visudo -c,但是將其作為驗證步驟不起作用。Ansible 需要%s佔位符,即使不需要,也會在將文件複製到位之前完成驗證,這樣visudo -c就不會擷取它。

我讓它工作了。這就是我所做的。首先,我添加了一組 Ansible 任務以在 /etc/sudoers.stage.d 處創建一個暫存目錄並將 /etc/sudoers.d 的內容複製到其中。然後,我將文件上傳到此暫存區域,如果其中任何一個發生更改,請執行自定義腳本來啟動它們。

這就是劇本中的邏輯現在的樣子

- name: delete staging area
 file:
   path: "/etc/sudoers.stage.d"
   state: absent
 changed_when: false

- name: copy /etc/sudoers.d to staging area
 shell: "cp -rp /etc/sudoers.d /etc/sudoers.stage.d"
 changed_when: false

- name: stage sudoers files
 copy:
   src: "{{item}}"
   dest: "/etc/sudoers.stage.d/{{item}}"
   backup: yes
   owner: root
   group: root
   mode: 0440
   validate: /usr/sbin/visudo -cf %s
 with_items:
   - admins
   - apache
   - monitor
 register: sudoers_d

- block:
 - name: push out activate script
   copy:
     src: activate_sudoers.sh
     dest: /usr/local/bin/activate_sudoers.sh
     owner: root
     group: root
     mode: 0700

 - name: activate change
   shell: /bin/sh /usr/local/bin/activate_sudoers.sh /etc/sudoers.stage.d

 when: sudoers_d.changed

這就是 activate_sudoers.sh 腳本的樣子。

#!/bin/sh

function usage {
   echo "Usage: $0 <stage directory>" >&2
   exit 1
}

function abort {
   echo "*** Error detected" >&2
   [ "$#" -gt 0 ] && echo "***" $@ >&2
   exit 1
}

PATH=/usr/bin:/bin:/usr/sbin:/sbin
export PATH

test $# -eq 1 || usage
test -d "$1" || abort "Stage directory $1: missing or not a directory"
test -d /etc/sudoers.old.d && rm -rf /etc/sudoers.old.d
test -d /etc/sudoers.old.d && abort "Failed to remove /etc/sudoers.old.d"

mv /etc/sudoers.d /etc/sudoers.old.d \
 && mv "$1" /etc/sudoers.d \
 && visudo -c

if [ $? -eq 0 ]; then
   # Success - clean up
   rm -rf /etc/sudoers.old.d
   exit 0
else
   # Failure - roll back
   rm -rf /etc/sudoers.d
   mv /etc/sudoers.old.d /etc/sudoers.d
   abort "sudoers update failed"
fi

它比我希望的要長和復雜一些,但它完成了工作。希望這對遇到同樣問題的其他人有用。

你有沒有試過這個:

- copy:
   src: '{{ item }}'
   dest: '/etc/sudoers.d/{{ item }}'
   owner: root
   group: root
   mode: 0440
   validate: 'bash -c "cat /etc/sudoers %s | visudo -cf-"'

這個對我有用。

引用自:https://serverfault.com/questions/901491