ansible / ansible

Ansible is a radically simple IT automation platform that makes your applications and systems easier to deploy and maintain. Automate everything from code deployment to network configuration to cloud management, in a language that approaches plain English, using SSH, with no agents to install on remote systems. https://docs.ansible.com.
https://www.ansible.com/
GNU General Public License v3.0
63.09k stars 23.93k forks source link

Add rootdir support for user module on FreeBSD #84370

Open TLINDEN opened 3 days ago

TLINDEN commented 3 days ago

Summary

I am building a bastille chroot on FreeBSD using ansible. I need to maintain lots of users inside the jail, but the builtin users module just works on the target host system.

I could add the jail dynamically to the inventory and then execute the user module on it, but it would be much easier to just provide a root dir variable to the users module, since it already supports this kind of thing. From the man page:

 -R rootdir    Specifies an alternate root directory within which pw will
              operate.  Any paths specified will be relative to rootdir.

Issue Type

Feature Idea

Component Name

user

Additional Information

- name: Add a jail user
  ansible.builtin.user:
    name: james18
    shell: /bin/zsh
    groups: developers
    rootdir: /usr/local/bastille/jails/jumper/root

Code of Conduct

ansibot commented 3 days ago

Files identified in the description:

If these files are incorrect, please update the component name section of the description or use the component bot command.

TLINDEN commented 3 days ago

Unfortunately my patch doesn't work yet:

msg: 'Unsupported parameters for (ansible.builtin.user) module: rootdir. Supported parameters include: [...]

It's unclear to me where to define supported parameters.

TLINDEN commented 3 days ago

Ok, found it, now the new code runs w/o errors, but it's still creating the user on the host, not inside the jail. Output is:

TASK [pubnix : Create users] **************************************************************************************************************************************************************************************                                                                               
task path: /home/scip/dev/bsdnix/roles/pubnix/tasks/main.yaml:45                                                                                                                                                                                                  
Using module file /usr/lib/python3/dist-packages/ansible/modules/user.py                                                                         
Pipelining is enabled.
<2a01:4f8:c013:6513::1> ESTABLISH SSH CONNECTION FOR USER: root                                                                                  
<2a01:4f8:c013:6513::1> SSH: EXEC ssh -vvv -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="root"' -o ConnectTime
out=10 -o 'ControlPath="/home/scip/.ansible/cp/389c0dea8b"' 2a01:4f8:c013:6513::1 '/bin/sh -c '"'"'/usr/local/bin/python3 && sleep 0'"'"''       
<2a01:4f8:c013:6513::1> (0, b'\n{"name": "scip", "state": "present", "system": false, "create_home": true, "changed": true, "uid": 1003, "group": 1003, "comment": "User &", "home": "/home/scip", "shell": "/usr/local/bin/bash", "groups": "wheel", "invocation": {"module_args": {"name": "scip
", "shell": "/usr/local/bin/bash", "groups": ["wheel"], "rootdir": "/usr/local/bastille/jails/pubnix/root", "state": "present", "non_unique": false, "force": false, "remove": false, "create_home": true, "system": false, "move_home": false, "append": false, "ssh_key_bits": 0, "ssh_key_type"
: "rsa", "ssh_key_comment": "ansible-generated on shell.daemon.de", "update_password": "always", "uid": null, "group": null, "comment": null, "home": null, "password": null, "login_class": null, "password_expire_max": null, "password_expire_min": null, "password_expire_warn": null, "hidden
": null, "seuser": null, "skeleton": null, "generate_ssh_key": null, "ssh_key_file": null, "ssh_key_passphrase": null, "expires": null, "password_lock": null, "local": null, "profile": null, "authorization": null, "role": null, "umask": null, "password_expire_account_disable": null, "uid_m
in": null, "uid_max": null}}}\n', b"OpenSSH_9.6p1 Ubuntu-3ubuntu13.5, OpenSSL 3.0.13 30 Jan 2024\r\ndebug1: Reading configuration data /home/scip/.ssh/config\r\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug1: /etc/ssh/ssh_config line 19: include /etc/ssh/ssh_config.d/*.co
nf matched no files\r\ndebug1: /etc/ssh/ssh_config line 21: Applying options for *\r\ndebug2: resolve_canonicalize: hostname 2a01:4f8:c013:6513::1 is address\r\ndebug3: expanded UserKnownHostsFile '~/.ssh/known_hosts' -> '/home/scip/.ssh/known_hosts'\r\ndebug3: expanded UserKnownHostsFile 
'~/.ssh/known_hosts2' -> '/home/scip/.ssh/known_hosts2'\r\ndebug1: auto-mux: Trying existing master at '/home/scip/.ansible/cp/389c0dea8b'\r\ndebug2: fd 3 setting O_NONBLOCK\r\ndebug2: mux_client_hello_exchange: master version 4\r\ndebug3: mux_client_forwards: request forwardings: 0 local,
 0 remote\r\ndebug3: mux_client_request_session: entering\r\ndebug3: mux_client_request_alive: entering\r\ndebug3: mux_client_request_alive: done pid = 184245\r\ndebug3: mux_client_request_session: session request sent\r\ndebug1: mux_client_request_session: master session id: 2\r\ndebug3: 
mux_client_read_packet_timeout: read header failed: Broken pipe\r\ndebug2: Received exit status from master 0\r\n")
changed: [shell.daemon.de] => (item={'name': 'scip', 'groups': 'wheel', 'shell': '/usr/local/bin/bash', 'rootdir': '/usr/local/bastille/jails/pubnix/root'}) => changed=true                                                                                                                      
  ansible_loop_var: item        
  comment: User &        
  create_home: true
  group: 1003      
  groups: wheel 
  home: /home/scip                                                                                       
  invocation:          
    module_args:                
      append: false 
      authorization: null
      comment: null                                                                                      
      create_home: true                    
      expires: null            
      force: false             
      generate_ssh_key: null    
      group: null        
      groups:      
      - wheel      
      hidden: null 
      home: null                                                                                                                 
      local: null            
      login_class: null         
      move_home: false
      name: scip     
      non_unique: false                                                                                                          
      password: null          
      password_expire_account_disable: null
      password_expire_max: null
      password_expire_min: null
      password_expire_warn: null
      password_lock: null
      profile: null                                             
      remove: false                                                                                                                                                                                                
      role: null                                                                                                                                                                                                   
      rootdir: /usr/local/bastille/jails/pubnix/root                                                                                             
      seuser: null                                              
      shell: /usr/local/bin/bash                  
      skeleton: null
      ssh_key_bits: 0                                           
      ssh_key_comment: ansible-generated on shell.daemon.de                                                                                      
      ssh_key_file: null                                        
      ssh_key_passphrase: null                                  
      ssh_key_type: rsa                                         
      state: present                                            
      system: false                                             
      uid: null                                                         
      uid_max: null                                                                                                                                                                                                                                               
      uid_min: null                                                                                                                                                                                                                                               
      umask: null                                                       
      update_password: always                                           
  item:                                                         
    groups: wheel                                               
    name: scip                                                          
    rootdir: /usr/local/bastille/jails/pubnix/root                      
    shell: /usr/local/bin/bash                                          
  name: scip                                                            
  shell: /usr/local/bin/bash                                            
  state: present                                                        
  system: false                                                         
  uid: 1003    

But the user isn't there:

# looking into the jail:
root@shell: # jexec 1 id scip
id: scip: no such user

# looking on the host:
root@shell: # id scip
uid=1003(scip) gid=1003(scip) groups=1003(scip),0(wheel)

That's because self.rootdir in FreeBsdUser.create_user is None so the -R options is not being appended. But as can be seen from the output above, rootdir is defined.

I'm stuck :(

TLINDEN commented 3 days ago

Now, with the latest commit it works:

TASK [pubnix : Create users] *************************************************************************************************************************************************************
task path: /home/scip/dev/bsdnix/roles/pubnix/tasks/main.yaml:45
Using module file /usr/lib/python3/dist-packages/ansible/modules/user.py
Pipelining is enabled.
<2a01:4f8:c013:6513::1> ESTABLISH SSH CONNECTION FOR USER: root
<2a01:4f8:c013:6513::1> SSH: EXEC ssh -vvv -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="root"' -o ConnectTimeout=10 -o 'ControlPath="/home/scip/.ansible/cp/389c0dea8b"' 2a01:4f8:c013:6513::1 '/bin/sh -c '"'"'/usr/local/bin/python3 && sleep 0'"'"''
<2a01:4f8:c013:6513::1> (0, b'\n{"name": "scip", "state": "present", "system": false, "create_home": true, "changed": true, "invocation": {"module_args": {"name": "scip", "shell": "/usr/local/bin/bash", "groups": ["wheel"], "rootdir": "/usr/local/bastille/jails/pubnix/root", "state": "present", "non_unique": false, "force": false, "remove": false, "create_home": true, "system": false, "move_home": false, "append": false, "ssh_key_bits": 0, "ssh_key_type": "rsa", "ssh_key_comment": "ansible-generated on shell.daemon.de", "update_password": "always", "uid": null, "group": null, "comment": null, "home": null, "password": null, "login_class": null, "password_expire_max": null, "password_expire_min": null, "password_expire_warn": null, "hidden": null, "seuser": null, "skeleton": null, "generate_ssh_key": null, "ssh_key_file": null, "ssh_key_passphrase": null, "expires": null, "password_lock": null, "local": null, "profile": null, "authorization": null, "role": null, "umask": null, "password_expire_account_disable": null, "uid_min": null, "uid_max": null}}}\n', b"OpenSSH_9.6p1 Ubuntu-3ubuntu13.5, OpenSSL 3.0.13 30 Jan 2024\r\ndebug1: Reading configuration data /home/scip/.ssh/config\r\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug1: /etc/ssh/ssh_config line 19: include /etc/ssh/ssh_config.d/*.conf matched no files\r\ndebug1: /etc/ssh/ssh_config line 21: Applying options for *\r\ndebug2: resolve_canonicalize: hostname 2a01:4f8:c013:6513::1 is address\r\ndebug3: expanded UserKnownHostsFile '~/.ssh/known_hosts' -> '/home/scip/.ssh/known_hosts'\r\ndebug3: expanded UserKnownHostsFile '~/.ssh/known_hosts2' -> '/home/scip/.ssh/known_hosts2'\r\ndebug1: auto-mux: Trying existing master at '/home/scip/.ansible/cp/389c0dea8b'\r\ndebug2: fd 3 setting O_NONBLOCK\r\ndebug2: mux_client_hello_exchange: master version 4\r\ndebug3: mux_client_forwards: request forwardings: 0 local, 0 remote\r\ndebug3: mux_client_request_session: entering\r\ndebug3: mux_client_request_alive: entering\r\ndebug3: mux_client_request_alive: done pid = 209306\r\ndebug3: mux_client_request_session: session request sent\r\ndebug1: mux_client_request_session: master session id: 2\r\ndebug3: mux_client_read_packet_timeout: read header failed: Broken pipe\r\ndebug2: Received exit status from master 0\r\n")
changed: [shell.daemon.de] => (item={'name': 'scip', 'groups': 'wheel', 'shell': '/usr/local/bin/bash', 'rootdir': '/usr/local/bastille/jails/pubnix/root'}) => changed=true 
  ansible_loop_var: item
  create_home: true
  invocation:
    module_args:
      append: false
      authorization: null
      comment: null
      create_home: true
      expires: null
      force: false
      generate_ssh_key: null
      group: null
      groups:
      - wheel
      hidden: null
      home: null
      local: null
      login_class: null
      move_home: false
      name: scip
      non_unique: false
      password: null
      password_expire_account_disable: null
      password_expire_max: null
      password_expire_min: null
      password_expire_warn: null
      password_lock: null
      profile: null
      remove: false
      role: null
      rootdir: /usr/local/bastille/jails/pubnix/root
      seuser: null
      shell: /usr/local/bin/bash
      skeleton: null
      ssh_key_bits: 0
      ssh_key_comment: ansible-generated on shell.daemon.de
      ssh_key_file: null
      ssh_key_passphrase: null
      ssh_key_type: rsa
      state: present
      system: false
      uid: null
      uid_max: null
      uid_min: null
      umask: null
      update_password: always
  item:
    groups: wheel
    name: scip
    rootdir: /usr/local/bastille/jails/pubnix/root
    shell: /usr/local/bin/bash
  name: scip
  state: present
  system: false

System state after deployment:

jexec 1 id scip
uid=1001(scip) gid=1001(scip) groups=1001(scip),0(wheel)
TLINDEN commented 2 days ago

Arg, there are too many dependencies in host user management functions like user_exists(), get_pwd_info() etc. None of these is able to work from inside a jail. Also, some functions use the pw tool (those specifically written for FreeBSD), but others use system calls such as getpwnam().

So I'd have to re-implement almost all those functions in the FreeBsdUser class to support it and use pw tool for all supported functions.

Nope, I don't have the time for this unfortunately. If someone else wants to work on this: feel free to take over the PR.

I am just using a simple script to maintain jail users now:

- name: Create users
  loop: "{{ users }}"
  shell: |
    if pw -V {{ item.rootdir }}/etc user show {{ item.name }} > /dev/null 2>&1; then \
       pw -V {{ item.rootdir }}/etc user mod  {{ item.name }} -d /home/{{ item.name }} -G {{ item.groups }} -m -s {{ item.shell }}; \
    else \
       pw -V {{ item.rootdir }}/etc user add  {{ item.name }} -d /home/{{ item.name }} -G {{ item.groups }} -m -s {{ item.shell }}; \
    fi

This fixes the issue for me.