mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
User module - Check local database when local is specified in the task (#51088)
The output of pw.getpwnam() does not distinbuish between local and remote accounts. It will return a result if an account exists locally or in the directory. When local is set to True in the task parameters, look through the local password database explicitly. * Ensure luseradd is present for tests * Add docs and warnings about local mode
This commit is contained in:
parent
43a44e6f35
commit
1e595493d9
3 changed files with 118 additions and 7 deletions
2
changelogs/user-read-passwd-when-local.yaml
Normal file
2
changelogs/user-read-passwd-when-local.yaml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
bugfixes:
|
||||||
|
- user - fix a bug when checking if a local user account exists on a system using directory authentication (https://github.com/ansible/ansible/issues/50947, https://github.com/ansible/ansible/issues/38206)
|
|
@ -206,7 +206,9 @@ options:
|
||||||
- Forces the use of "local" command alternatives on platforms that implement it.
|
- Forces the use of "local" command alternatives on platforms that implement it.
|
||||||
- This is useful in environments that use centralized authentification when you want to manipulate the local users
|
- This is useful in environments that use centralized authentification when you want to manipulate the local users
|
||||||
(i.e. it uses C(luseradd) instead of C(useradd)).
|
(i.e. it uses C(luseradd) instead of C(useradd)).
|
||||||
- This requires that these commands exist on the targeted host, otherwise it will be a fatal error.
|
- This will check C(/etc/passwd) for an existing account before invoking commands. If the local account database
|
||||||
|
exists somewhere other than C(/etc/passwd), this setting will not work properly.
|
||||||
|
- This requires that the above commands as well as C(/etc/passwd) must exist on the target host, otherwise it will be a fatal error.
|
||||||
type: bool
|
type: bool
|
||||||
default: no
|
default: no
|
||||||
version_added: "2.4"
|
version_added: "2.4"
|
||||||
|
@ -446,6 +448,7 @@ class User(object):
|
||||||
|
|
||||||
platform = 'Generic'
|
platform = 'Generic'
|
||||||
distribution = None
|
distribution = None
|
||||||
|
PASSWORDFILE = '/etc/passwd'
|
||||||
SHADOWFILE = '/etc/shadow'
|
SHADOWFILE = '/etc/shadow'
|
||||||
SHADOWFILE_EXPIRE_INDEX = 7
|
SHADOWFILE_EXPIRE_INDEX = 7
|
||||||
LOGIN_DEFS = '/etc/login.defs'
|
LOGIN_DEFS = '/etc/login.defs'
|
||||||
|
@ -840,6 +843,30 @@ class User(object):
|
||||||
return groups
|
return groups
|
||||||
|
|
||||||
def user_exists(self):
|
def user_exists(self):
|
||||||
|
# The pwd module does not distinguish between local and directory accounts.
|
||||||
|
# It's output cannot be used to determine whether or not an account exists locally.
|
||||||
|
# It returns True if the account exists locally or in the directory, so instead
|
||||||
|
# look in the local PASSWORD file for an existing account.
|
||||||
|
if self.local:
|
||||||
|
if not os.path.exists(self.PASSWORDFILE):
|
||||||
|
self.module.fail_json(msg="'local: true' specified but unable to find local account file {0} to parse.".format(self.PASSWORDFILE))
|
||||||
|
|
||||||
|
exists = False
|
||||||
|
name_test = '{0}:'.format(self.name)
|
||||||
|
with open(self.PASSWORDFILE, 'rb') as f:
|
||||||
|
reversed_lines = f.readlines()[::-1]
|
||||||
|
for line in reversed_lines:
|
||||||
|
if line.startswith(to_bytes(name_test)):
|
||||||
|
exists = True
|
||||||
|
break
|
||||||
|
|
||||||
|
self.module.warn(
|
||||||
|
"'local: true' specified and user was not found in {file}. "
|
||||||
|
"The local user account may already exist if the local account database exists somewhere other than {file}.".format(file=self.PASSWORDFILE))
|
||||||
|
|
||||||
|
return exists
|
||||||
|
|
||||||
|
else:
|
||||||
try:
|
try:
|
||||||
if pwd.getpwnam(self.name):
|
if pwd.getpwnam(self.name):
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -255,7 +255,7 @@
|
||||||
mode = oct(0o777 & ~umask)
|
mode = oct(0o777 & ~umask)
|
||||||
print(str(mode).replace('o', ''))
|
print(str(mode).replace('o', ''))
|
||||||
args:
|
args:
|
||||||
executable: python
|
executable: "{{ ansible_facts.python.executable }}"
|
||||||
register: user_login_defs_umask
|
register: user_login_defs_umask
|
||||||
|
|
||||||
- name: validate that user home dir is created
|
- name: validate that user home dir is created
|
||||||
|
@ -775,3 +775,85 @@
|
||||||
password_lock: no
|
password_lock: no
|
||||||
|
|
||||||
when: ansible_facts['system'] in ['FreeBSD', 'OpenBSD', 'Linux']
|
when: ansible_facts['system'] in ['FreeBSD', 'OpenBSD', 'Linux']
|
||||||
|
|
||||||
|
|
||||||
|
## Check local mode
|
||||||
|
# Even if we don't have a system that is bound to a directory, it's useful
|
||||||
|
# to run with local: true to exercise the code path that reads through the local
|
||||||
|
# user database file.
|
||||||
|
# https://github.com/ansible/ansible/issues/50947
|
||||||
|
|
||||||
|
- name: Create /etc/gshadow
|
||||||
|
file:
|
||||||
|
path: /etc/gshadow
|
||||||
|
state: touch
|
||||||
|
when: ansible_facts.os_family == 'Suse'
|
||||||
|
tags:
|
||||||
|
- user_test_local_mode
|
||||||
|
|
||||||
|
- name: Create /etc/libuser.conf
|
||||||
|
file:
|
||||||
|
path: /etc/libuser.conf
|
||||||
|
state: touch
|
||||||
|
when:
|
||||||
|
- ansible_facts.distribution == 'Ubuntu'
|
||||||
|
- ansible_facts.distribution_major_version is version_compare('16', '==')
|
||||||
|
tags:
|
||||||
|
- user_test_local_mode
|
||||||
|
|
||||||
|
- name: Ensure luseradd is present
|
||||||
|
action: "{{ ansible_facts.pkg_mgr }}"
|
||||||
|
args:
|
||||||
|
name: libuser
|
||||||
|
state: present
|
||||||
|
when: ansible_facts.system in ['Linux']
|
||||||
|
tags:
|
||||||
|
- user_test_local_mode
|
||||||
|
|
||||||
|
- name: Create local_ansibulluser
|
||||||
|
user:
|
||||||
|
name: local_ansibulluser
|
||||||
|
state: present
|
||||||
|
local: yes
|
||||||
|
register: local_user_test_1
|
||||||
|
tags:
|
||||||
|
- user_test_local_mode
|
||||||
|
|
||||||
|
- name: Create local_ansibulluser again
|
||||||
|
user:
|
||||||
|
name: local_ansibulluser
|
||||||
|
state: present
|
||||||
|
local: yes
|
||||||
|
register: local_user_test_2
|
||||||
|
tags:
|
||||||
|
- user_test_local_mode
|
||||||
|
|
||||||
|
- name: Remove local_ansibulluser
|
||||||
|
user:
|
||||||
|
name: local_ansibulluser
|
||||||
|
state: absent
|
||||||
|
remove: yes
|
||||||
|
local: yes
|
||||||
|
register: local_user_test_3
|
||||||
|
tags:
|
||||||
|
- user_test_local_mode
|
||||||
|
|
||||||
|
- name: Remove local_ansibulluser again
|
||||||
|
user:
|
||||||
|
name: local_ansibulluser
|
||||||
|
state: absent
|
||||||
|
remove: yes
|
||||||
|
local: yes
|
||||||
|
register: local_user_test_4
|
||||||
|
tags:
|
||||||
|
- user_test_local_mode
|
||||||
|
|
||||||
|
- name: Ensure local user accounts were created
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- local_user_test_1 is changed
|
||||||
|
- local_user_test_2 is not changed
|
||||||
|
- local_user_test_3 is changed
|
||||||
|
- local_user_test_4 is not changed
|
||||||
|
tags:
|
||||||
|
- user_test_local_mode
|
||||||
|
|
Loading…
Reference in a new issue