mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
* feat: sudoers module supports runas parameter with default of root
* fix: sudoers tests now pass
* chore: add changelog fragment for 4380
* fix: runas feature now a non-breaking change wh no def with no default
* fix: no trailing space in sudoers.py
* Update plugins/modules/system/sudoers.py
Co-authored-by: Felix Fontein <felix@fontein.de>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 17fe813c18
)
Co-authored-by: doubletwist13 <doubletwist@fearthepenguin.net>
This commit is contained in:
parent
743e9c851f
commit
e9b3705809
3 changed files with 40 additions and 1 deletions
4
changelogs/fragments/4380-sudoers-runas-parameter.yml
Normal file
4
changelogs/fragments/4380-sudoers-runas-parameter.yml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
minor_changes:
|
||||||
|
- sudoers - add support for ``runas`` parameter
|
||||||
|
(https://github.com/ansible-collections/community.general/issues/4379).
|
|
@ -41,6 +41,11 @@ options:
|
||||||
- Whether a password will be required to run the sudo'd command.
|
- Whether a password will be required to run the sudo'd command.
|
||||||
default: true
|
default: true
|
||||||
type: bool
|
type: bool
|
||||||
|
runas:
|
||||||
|
description:
|
||||||
|
- Specify the target user the command(s) will run as.
|
||||||
|
type: str
|
||||||
|
version_added: 4.7.0
|
||||||
sudoers_path:
|
sudoers_path:
|
||||||
description:
|
description:
|
||||||
- The path which sudoers config files will be managed in.
|
- The path which sudoers config files will be managed in.
|
||||||
|
@ -69,6 +74,14 @@ EXAMPLES = '''
|
||||||
user: backup
|
user: backup
|
||||||
commands: /usr/local/bin/backup
|
commands: /usr/local/bin/backup
|
||||||
|
|
||||||
|
- name: Allow the bob user to run any commands as alice with sudo -u alice
|
||||||
|
community.general.sudoers:
|
||||||
|
name: bob-do-as-alice
|
||||||
|
state: present
|
||||||
|
user: bob
|
||||||
|
runas: alice
|
||||||
|
commands: ANY
|
||||||
|
|
||||||
- name: >-
|
- name: >-
|
||||||
Allow the monitoring group to run sudo /usr/local/bin/gather-app-metrics
|
Allow the monitoring group to run sudo /usr/local/bin/gather-app-metrics
|
||||||
without requiring a password
|
without requiring a password
|
||||||
|
@ -108,6 +121,7 @@ class Sudoers(object):
|
||||||
self.group = module.params['group']
|
self.group = module.params['group']
|
||||||
self.state = module.params['state']
|
self.state = module.params['state']
|
||||||
self.nopassword = module.params['nopassword']
|
self.nopassword = module.params['nopassword']
|
||||||
|
self.runas = module.params['runas']
|
||||||
self.sudoers_path = module.params['sudoers_path']
|
self.sudoers_path = module.params['sudoers_path']
|
||||||
self.file = os.path.join(self.sudoers_path, self.name)
|
self.file = os.path.join(self.sudoers_path, self.name)
|
||||||
self.commands = module.params['commands']
|
self.commands = module.params['commands']
|
||||||
|
@ -140,7 +154,8 @@ class Sudoers(object):
|
||||||
|
|
||||||
commands_str = ', '.join(self.commands)
|
commands_str = ', '.join(self.commands)
|
||||||
nopasswd_str = 'NOPASSWD:' if self.nopassword else ''
|
nopasswd_str = 'NOPASSWD:' if self.nopassword else ''
|
||||||
return "{owner} ALL={nopasswd} {commands}\n".format(owner=owner, nopasswd=nopasswd_str, commands=commands_str)
|
runas_str = '({runas})'.format(runas=self.runas) if self.runas is not None else ''
|
||||||
|
return "{owner} ALL={runas}{nopasswd} {commands}\n".format(owner=owner, runas=runas_str, nopasswd=nopasswd_str, commands=commands_str)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
if self.state == 'absent' and self.exists():
|
if self.state == 'absent' and self.exists():
|
||||||
|
@ -168,6 +183,10 @@ def main():
|
||||||
'type': 'bool',
|
'type': 'bool',
|
||||||
'default': True,
|
'default': True,
|
||||||
},
|
},
|
||||||
|
'runas': {
|
||||||
|
'type': 'str',
|
||||||
|
'default': None,
|
||||||
|
},
|
||||||
'sudoers_path': {
|
'sudoers_path': {
|
||||||
'type': 'str',
|
'type': 'str',
|
||||||
'default': '/etc/sudoers.d',
|
'default': '/etc/sudoers.d',
|
||||||
|
|
|
@ -102,6 +102,21 @@
|
||||||
src: "{{ alt_sudoers_path }}/my-sudo-rule-5"
|
src: "{{ alt_sudoers_path }}/my-sudo-rule-5"
|
||||||
register: rule_5_contents
|
register: rule_5_contents
|
||||||
|
|
||||||
|
- name: Create rule to runas another user
|
||||||
|
community.general.sudoers:
|
||||||
|
name: my-sudo-rule-6
|
||||||
|
state: present
|
||||||
|
user: alice
|
||||||
|
commands: /usr/local/bin/command
|
||||||
|
runas: bob
|
||||||
|
sudoers_path: "{{ sudoers_path }}"
|
||||||
|
register: rule_6
|
||||||
|
|
||||||
|
- name: Grab contents of my-sudo-rule-6 (in alternative directory)
|
||||||
|
ansible.builtin.slurp:
|
||||||
|
src: "{{ sudoers_path }}/my-sudo-rule-6"
|
||||||
|
register: rule_6_contents
|
||||||
|
|
||||||
|
|
||||||
- name: Revoke rule 1
|
- name: Revoke rule 1
|
||||||
community.general.sudoers:
|
community.general.sudoers:
|
||||||
|
@ -133,6 +148,7 @@
|
||||||
- "rule_3_contents['content'] | b64decode == 'alice ALL= /usr/local/bin/command\n'"
|
- "rule_3_contents['content'] | b64decode == 'alice ALL= /usr/local/bin/command\n'"
|
||||||
- "rule_4_contents['content'] | b64decode == '%students ALL=NOPASSWD: /usr/local/bin/command\n'"
|
- "rule_4_contents['content'] | b64decode == '%students ALL=NOPASSWD: /usr/local/bin/command\n'"
|
||||||
- "rule_5_contents['content'] | b64decode == 'alice ALL=NOPASSWD: /usr/local/bin/command\n'"
|
- "rule_5_contents['content'] | b64decode == 'alice ALL=NOPASSWD: /usr/local/bin/command\n'"
|
||||||
|
- "rule_6_contents['content'] | b64decode == 'alice ALL=(bob)NOPASSWD: /usr/local/bin/command\n'"
|
||||||
|
|
||||||
- name: Check stats
|
- name: Check stats
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
|
|
Loading…
Reference in a new issue