Ansible can't create SELinux module

Hi,

I’m currently trying to create a SELinux module using Ansible.

Here’s the command I’m using interactively in the shell:

# cd /var/tmp
# ausearch -c 'dnsmasq' --raw | audit2allow -M my_dnsmasq
# ls -l my_dnsmasq.*
-rw-r--r--. 1 root root 945 May 23 07:57 my_dnsmasq.pp
-rw-r--r--. 1 root root 179 May 23 07:57 my_dnsmasq.te

Here’s how I translated this in Ansible (I’ll worry about idempotency later):

- name: Generate SELinux module for Dnsmasq
  ansible.builtin.shell: |
    ausearch -c 'dnsmasq' --raw | audit2allow -M my_dnsmasq
  args:
    chdir: /var/tmp/
    executable: /bin/bash

Curiously enough, when I run this task it looks like something has changed and I get no error message. On the other hand, there’s no resulting my_dnsmasq* file anywhere on the system.

Any idea what’s going on here?

OK now here’s something very weird. It looks like Ansible’s shell module can’t handle commands like ausearch or audit2allow.

I just tested this:

- name: Test ausearch
  ansible.builtin.shell:
    cmd: /usr/sbin/ausearch -c dnsmasq --raw
    chdir: /var/tmp/
    executable: /bin/bash

And here’s what I get:

fatal: [localhost]: FAILED! => {"changed": true, "cmd": "/usr/sbin/ausearch -c dnsmasq --raw", "delta": "0:
00:00.023751", "end": "2025-05-23 08:53:20.364510", "msg": "non-zero return code", "rc": 1, "start": "2025-
05-23 08:53:20.340759", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}

I’m beginning to understand why folks choose to disable SELinux.

That’s because ansible assumes that a return value of 0 would be a success, and perhaps in this case 1 is success (the rc value), so you can do:

      register: ausearch
      failed_when:
        - ausearch.rc != 1

the ausearch can be any text you like, as long as it’s followed by the .rc or even set:

      changed_when: ausearch.rc == 1

instead of failed_when. You may need a combo of both.

Is the *.te (ASCII) file system-specific, or would the same rule/policy apply to all systems?
If it is not system-specific, then you don’t need to create it in every host.

Long ago, I did use crude (for cups – I have no issues with dnsmasq):

  - name: Remove SELinux policy package
    command: semodule -r my-cupspkhelper
    failed_when: false

  - name: Copy SELinux type enforcement file
    copy:
      src:  my-cupspkhelper.te
      dest: /tmp/

  - name: Compile SELinux module file
    command: checkmodule -M -m -o /tmp/my-cupspkhelper.mod /tmp/my-cupspkhelper.te

  - name: Build SELinux policy package
    command: semodule_package -o /tmp/my-cupspkhelper.pp -m /tmp/my-cupspkhelper.mod

  - name: Load SELinux policy package
    command: semodule -i /tmp/my-cupspkhelper.pp

  - name: Remove temporary files
    file:
      path:  /tmp/my-cupspkhelper.*
      state: absent

Now here’s the thing. The ausearch command does return 0 when invoked directly in a shell:

# ausearch -c dnsmasq --raw
type=AVC msg=audit(1747976451.939:101): avc:  denied  { getattr } for  pid=2309 comm="dnsmasq" path="/etc/resolv.conf" dev="dm-2" ino=67884332 scontext=system_u:system_r:dnsmasq_t:s0 tcontext=unconfined_u:object_r:admin_home_t:s0 tclass=file permissive=0
...
# echo $?
0

But when I try to launch the same (!) command using Ansible’s shell module, it fails.

I’m confused.

Best way instead of comparing it to the console output when you do it manually, is to use the ansible register function, and then use that in a following task with debug to see all the information. Then use that with the changed_when or failed_when options. I have had to do this when using dnf commands in certain situations.

Yeah, I know. But it doesn’t even get to that. The command fails, plain and simple. Output or not. Just try it out and see for yourself.

Well I don’t have your problem with dnsmasq because I don’t use it. And even if I run ausearch -c dnsmasq --raw, I don’t have anything to work with to replicate your problem.

Does the following possibly work? Ignores error codes.

(ausearch -c 'dnsmasq' --raw | audit2allow -M my_dnsmasq) || true

Even ignoring error codes doesn’t work. Here’s the thing:

- name: Generate SELinux module for Dnsmasq
  ansible.builtin.shell:
    cmd: /usr/sbin/ausearch -c dnsmasq --raw
    chdir: /var/tmp/
    executable: /bin/bash
  register: ausearch_output
  failed_when: false

- name: Display the output of the ausearch command
  ansible.builtin.debug:
    var: ausearch_output.stdout_lines

The ausearch command works perfectly directly in a shell and displays a few lines of output. But when run from Ansible, there’s no output.

At this point I’m giving up. I simply build the SELinux module by hand and put this stuff in Ansible’s README file.

Thanks everybody for your input.