Search code examples
ansiblesudoers

Why is this ansible lineinfile command to check for a line in /etc/sudoers failing when a very similar one is succeeding?


I have Kodi running on a Raspberry Pi, for which I'm writing an Ansible playbook. This playbook includes two tasks that check that a line is present in /etc/sudoers, with one passing consistently but the other failing consistently. I can't seem to pinpoint the reason why; the syntax of the two tasks is exactly the same, and both lines are definitely in the /etc/sudoers file. I've included the relevant code below, any input would be highly appreciated.

/etc/sudoers:

# /etc/sudoers
#
# This file MUST be edited with the 'visudo' command as root.
#
# See the man page for details on how to write a sudoers file.
#

Defaults        env_reset

# Host alias specification

# User alias specification

# Cmnd alias specification
Cmnd_Alias      SHUTDOWN = /sbin/shutdown, /sbin/reboot, /sbin/halt, /usr/bin/passwd
Cmnd_Alias      PERMISSIONS = /bin/chmod, /bin/chown
# User privilege specification
root    ALL=(ALL) ALL
pi      ALL=(ALL) NOPASSWD: ALL
debian-transmission     ALL=(ALL) NOPASSWD: PERMISSIONS
Defaults env_keep += "RPI_UPDATE_UNSUPPORTED"
# Allow members of group sudo to execute any command
# (Note that later entries override this, so you might need to move
# it further down)
%sudo ALL=(ALL) ALL
#
#includedir /etc/sudoers.d

Relevant snippet from the playbook tasks:

- name: set pi permissions in /etc/sudoers                                      
  lineinfile: "dest=/etc/sudoers                                                
              state=present                                                     
              line='pi      ALL=(ALL) NOPASSWD: ALL'                            
              validate='visudo -cf %s'"                                         

- name: set debian-transmission permissions in /etc/sudoers                     
  lineinfile: "dest=/etc/sudoers                                                
              state=present                                                     
              line='debian-transmission     ALL=(ALL) NOPASSWD: PERMISSIONS'    
              validate='visudo -cf %s'"                                         

(I'm aware that the first task is unnecessary since that is the system default, but I added it while trying to figure out why the other task wasn't working just to prove a point.)

And here's the output when I run the playbook:

TASK: [kodi | start transmission-daemon again once settings.json has been copied] *** 
changed: [kodi]

TASK: [kodi | set pi permissions in /etc/sudoers] ***************************** 
ok: [kodi]

TASK: [kodi | set debian-transmission permissions in /etc/sudoers] ************ 
failed: [kodi] => {"cmd": "visudo -cf /tmp/tmpZNRBC3", "failed": true, "rc": 2}
msg: [Errno 2] No such file or directory

FATAL: all hosts have already failed -- aborting

Solution

  • I think you might be experiencing what is described in pull request #6652

    Replace visudo in your validate with full path to visudo and it should work.

    - name: set debian-transmission permissions in /etc/sudoers                     
      lineinfile: "dest=/etc/sudoers                                                
                  state=present                                                     
                  line='debian-transmission     ALL=(ALL) NOPASSWD: PERMISSIONS'    
                  validate='/usr/sbin/visudo -cf %s'"
    

    Your first task (with pi user privileges) works because desired line is already in the file and validation with visudo is not necessary. However a line with debian-transmission has to be added and goes through visudo validation (which is not found).