4

As part of a new server setup, I provision /etc/security/access.conf with user/group and src IP addresses of allowed ssh logins. This requires enabling pam_access in the /etc/pam.d/login and /etc/pam.d/sshd files (on Ubuntu anyway)

It seems Ansible has a module for modifying some of these rules but I can't get it to work. A stipulation of the modules is: "In order for a PAM rule to be modified, the type, control and module_path must match an existing rule." Does that mean if a rule is commented out, the pamd module will not work for enabling that line?

This is my current playbook. I've tried removing the with_items line in the task and using login for the name: parameter but that doesn't seem to work either:

---
- hosts: all
  gather_facts: False
  tasks:
    - name: modify pam_access in /etc/pam.d for sshd and login
      pamd:
        name   : "{{ item }}"
        type   : account
        control: required
        module_path: pam_access.so
      with_items:
        - login
        - sshd

And this the result of running it. It doesn't give me much to go on:

$ ansible-playbook tests/pam-access.yml -i 192.168.24.66,                                                             
SUDO password: 

PLAY [all] ***********************************************************************************************************

TASK [modify pam_access in /etc/pam.d for sshd and login] ************************************************************
ok: [192.168.24.66] => (item=login)
failed: [192.168.24.66] (item=sshd) => {"changed": false, "item": "sshd", "module_stderr": "Shared connection to 192.168.24.66 closed.\r\n", "module_stdout": "\r\nTraceback (most recent call last):\r\n  File \"/tmp/ansible_vzO7tZ/ansible_module_pamd.py\", line 691, in <module>\r\n    main()\r\n  File \"/tmp/ansible_vzO7tZ/ansible_module_pamd.py\", line 645, in main\r\n    pamd.load_rules_from_file()\r\n  File \"/tmp/ansible_vzO7tZ/ansible_module_pamd.py\", line 361, in load_rules_from_file\r\n    self.load_rules_from_string(stringline.replace(\"\\\\\\n\", \"\"))\r\n  File \"/tmp/ansible_vzO7tZ/ansible_module_pamd.py\", line 380, in load_rules_from_string\r\n    self.rules.append(PamdRule.rulefromstring(stringline))\r\n  File \"/tmp/ansible_vzO7tZ/ansible_module_pamd.py\", line 312, in rulefromstring\r\n    rule_type = result.group(1)\r\nAttributeError: 'NoneType' object has no attribute 'group'\r\n", "msg": "MODULE FAILURE", "rc": 1}

PLAY RECAP ***********************************************************************************************************
192.168.24.66              : ok=0    changed=0    unreachable=0    failed=1   
Server Fault
  • 3,714
  • 12
  • 54
  • 89

3 Answers3

2

Ended up going with lineinfile. If anyone has a better solution, would love to see it.

   ---
    - hosts: all
      gather_facts: False
      tasks:
        - name: un-comment or add pam_access in /etc/pam.d files
          lineinfile:
            path   : "{{ item }}"
            regexp : "#.*account.*required.*pam_access.so"
            line   : "account   required   pam_access.so"
            insertafter : ".*include.*common-auth"
            state  : present
          with_items:
            - /etc/pam.d/login
            - /etc/pam.d/sshd
Server Fault
  • 3,714
  • 12
  • 54
  • 89
1

Run it with the -vvvv option to see why it failed. In the sshd and login files, find a rule that already exists, then you use the state parameter to tell it to insert your new rule before or after the existing one. For example:

Try this format to create a new rule:

- name: modify pam_access in /etc/pam.d for sshd and login
  pamd:
    name: '{{ item }}'
    #Existing type, control, and module_path in sshd and login
    type: account
    control: required
    module_path: pam_nologin.so
    #New rule you want to add
    new_type: account
    new_control: required
    new_module_path: pam_access.so
    module_arguments: insert new args here, if applicable.  Remove if none.
    #Inserts rule before existing rule - can use after as well
    state: before 
  #loops through both files - if existing module exists in both.
  loop:
    - login
    - sshd

Or, you can find an existing rule and change the module/control:

 - name: Update pam_pwquality.so Rule Control 
  pamd:
    name: '{{ item }}'
    type: account
    control: required
    module_path: pam_nologin.so
    new_module_path: pam_pwquality.so
  loop:
    - login
    - sshd
Crystal
  • 11
  • 1
0

I ended up running pam-auth-update --force --enable profile mkhomedir sss tmpdir as an ansible command: (obviously requires libpam-mkhomedir, libpam-tmpdir and libpam-sss installed prior)

The benefits of this method is that pam-auth-update does all the files it needs to, in a perfectly consistent way.

The problem with this method is that ansible always reports "changed".

I am looking for a nicer solution to check if the configuration is actually required or not, so as to make the task perfectly idempotent, but unfortunately the only approach seems to be slurping all the remote files and checking them, or using a hack like lineinfile: with check_mode: yes and changed_when: false to see if the lines are present or not before running pam-auth-update.

My task:

- name: enable pam modules (requires libpam-mkhomedir, libpam-tmpdir, libpam-sss installed)
  command: pam-auth-update --force --enable profile mkhomedir sss tmpdir
Gostega
  • 181
  • 4