mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Add noexec support to sudoers (#7983)
* Add noexec support to sudoers * Add changelog fragment #7983 * Fix yml formatting in fragment 7983 * Apply suggestions from code review Co-authored-by: Felix Fontein <felix@fontein.de> --------- Co-authored-by: Felix Fontein <felix@fontein.de>
This commit is contained in:
parent
2a8da76907
commit
49bd9cbd3c
3 changed files with 40 additions and 1 deletions
2
changelogs/fragments/7983-sudoers-add-support-noexec.yml
Normal file
2
changelogs/fragments/7983-sudoers-add-support-noexec.yml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
minor_changes:
|
||||||
|
- sudoers - add support for the ``NOEXEC`` tag in sudoers rules (https://github.com/ansible-collections/community.general/pull/7983).
|
|
@ -45,6 +45,12 @@ options:
|
||||||
- The name of the sudoers rule.
|
- The name of the sudoers rule.
|
||||||
- This will be used for the filename for the sudoers file managed by this rule.
|
- This will be used for the filename for the sudoers file managed by this rule.
|
||||||
type: str
|
type: str
|
||||||
|
noexec:
|
||||||
|
description:
|
||||||
|
- Whether a command is prevented to run further commands itself.
|
||||||
|
default: false
|
||||||
|
type: bool
|
||||||
|
version_added: 8.4.0
|
||||||
nopassword:
|
nopassword:
|
||||||
description:
|
description:
|
||||||
- Whether a password will be required to run the sudo'd command.
|
- Whether a password will be required to run the sudo'd command.
|
||||||
|
@ -143,6 +149,15 @@ EXAMPLES = '''
|
||||||
user: alice
|
user: alice
|
||||||
commands: /usr/local/bin/upload
|
commands: /usr/local/bin/upload
|
||||||
setenv: true
|
setenv: true
|
||||||
|
|
||||||
|
- name: >-
|
||||||
|
Allow alice to sudo /usr/bin/less but prevent less from
|
||||||
|
running further commands itself
|
||||||
|
community.general.sudoers:
|
||||||
|
name: allow-alice-restricted-less
|
||||||
|
user: alice
|
||||||
|
commands: /usr/bin/less
|
||||||
|
noexec: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
@ -162,6 +177,7 @@ class Sudoers(object):
|
||||||
self.user = module.params['user']
|
self.user = module.params['user']
|
||||||
self.group = module.params['group']
|
self.group = module.params['group']
|
||||||
self.state = module.params['state']
|
self.state = module.params['state']
|
||||||
|
self.noexec = module.params['noexec']
|
||||||
self.nopassword = module.params['nopassword']
|
self.nopassword = module.params['nopassword']
|
||||||
self.setenv = module.params['setenv']
|
self.setenv = module.params['setenv']
|
||||||
self.host = module.params['host']
|
self.host = module.params['host']
|
||||||
|
@ -205,13 +221,15 @@ class Sudoers(object):
|
||||||
owner = '%{group}'.format(group=self.group)
|
owner = '%{group}'.format(group=self.group)
|
||||||
|
|
||||||
commands_str = ', '.join(self.commands)
|
commands_str = ', '.join(self.commands)
|
||||||
|
noexec_str = 'NOEXEC:' if self.noexec else ''
|
||||||
nopasswd_str = 'NOPASSWD:' if self.nopassword else ''
|
nopasswd_str = 'NOPASSWD:' if self.nopassword else ''
|
||||||
setenv_str = 'SETENV:' if self.setenv else ''
|
setenv_str = 'SETENV:' if self.setenv else ''
|
||||||
runas_str = '({runas})'.format(runas=self.runas) if self.runas is not None else ''
|
runas_str = '({runas})'.format(runas=self.runas) if self.runas is not None else ''
|
||||||
return "{owner} {host}={runas}{nopasswd}{setenv} {commands}\n".format(
|
return "{owner} {host}={runas}{noexec}{nopasswd}{setenv} {commands}\n".format(
|
||||||
owner=owner,
|
owner=owner,
|
||||||
host=self.host,
|
host=self.host,
|
||||||
runas=runas_str,
|
runas=runas_str,
|
||||||
|
noexec=noexec_str,
|
||||||
nopasswd=nopasswd_str,
|
nopasswd=nopasswd_str,
|
||||||
setenv=setenv_str,
|
setenv=setenv_str,
|
||||||
commands=commands_str
|
commands=commands_str
|
||||||
|
@ -258,6 +276,10 @@ def main():
|
||||||
'name': {
|
'name': {
|
||||||
'required': True,
|
'required': True,
|
||||||
},
|
},
|
||||||
|
'noexec': {
|
||||||
|
'type': 'bool',
|
||||||
|
'default': False,
|
||||||
|
},
|
||||||
'nopassword': {
|
'nopassword': {
|
||||||
'type': 'bool',
|
'type': 'bool',
|
||||||
'default': True,
|
'default': True,
|
||||||
|
|
|
@ -159,6 +159,20 @@
|
||||||
src: "{{ sudoers_path }}/my-sudo-rule-8"
|
src: "{{ sudoers_path }}/my-sudo-rule-8"
|
||||||
register: rule_8_contents
|
register: rule_8_contents
|
||||||
|
|
||||||
|
- name: Create rule with noexec parameters
|
||||||
|
community.general.sudoers:
|
||||||
|
name: my-sudo-rule-9
|
||||||
|
state: present
|
||||||
|
user: alice
|
||||||
|
commands: /usr/local/bin/command
|
||||||
|
noexec: true
|
||||||
|
register: rule_9
|
||||||
|
|
||||||
|
- name: Grab contents of my-sudo-rule-9
|
||||||
|
ansible.builtin.slurp:
|
||||||
|
src: "{{ sudoers_path }}/my-sudo-rule-9"
|
||||||
|
register: rule_9_contents
|
||||||
|
|
||||||
- name: Revoke rule 1
|
- name: Revoke rule 1
|
||||||
community.general.sudoers:
|
community.general.sudoers:
|
||||||
name: my-sudo-rule-1
|
name: my-sudo-rule-1
|
||||||
|
@ -257,6 +271,7 @@
|
||||||
- "rule_6_contents['content'] | b64decode == 'alice ALL=(bob)NOPASSWD: /usr/local/bin/command\n'"
|
- "rule_6_contents['content'] | b64decode == 'alice ALL=(bob)NOPASSWD: /usr/local/bin/command\n'"
|
||||||
- "rule_7_contents['content'] | b64decode == 'alice host-1=NOPASSWD: /usr/local/bin/command\n'"
|
- "rule_7_contents['content'] | b64decode == 'alice host-1=NOPASSWD: /usr/local/bin/command\n'"
|
||||||
- "rule_8_contents['content'] | b64decode == 'alice ALL=NOPASSWD:SETENV: /usr/local/bin/command\n'"
|
- "rule_8_contents['content'] | b64decode == 'alice ALL=NOPASSWD:SETENV: /usr/local/bin/command\n'"
|
||||||
|
- "rule_9_contents['content'] | b64decode == 'alice ALL=NOEXEC:NOPASSWD: /usr/local/bin/command\n'"
|
||||||
|
|
||||||
- name: Check revocation stat
|
- name: Check revocation stat
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
|
|
Loading…
Reference in a new issue