diff --git a/.ansible-lint b/.ansible-lint index 8986d7a..85ce45b 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -21,4 +21,4 @@ exclude_paths: - .markdownlint.yaml - examples/roles/ mock_roles: - - linux-system-roles.template + - linux-system-roles.sudo diff --git a/README.md b/README.md index dc99348..fcdd0d8 100644 --- a/README.md +++ b/README.md @@ -1,105 +1,308 @@ -# Role Name +# Sudo -[![ansible-lint.yml](https://github.com/linux-system-roles/template/actions/workflows/ansible-lint.yml/badge.svg)](https://github.com/linux-system-roles/template/actions/workflows/ansible-lint.yml) [![ansible-test.yml](https://github.com/linux-system-roles/template/actions/workflows/ansible-test.yml/badge.svg)](https://github.com/linux-system-roles/template/actions/workflows/ansible-test.yml) [![markdownlint.yml](https://github.com/linux-system-roles/template/actions/workflows/markdownlint.yml/badge.svg)](https://github.com/linux-system-roles/template/actions/workflows/markdownlint.yml) [![shellcheck.yml](https://github.com/linux-system-roles/template/actions/workflows/shellcheck.yml/badge.svg)](https://github.com/linux-system-roles/template/actions/workflows/shellcheck.yml) [![woke.yml](https://github.com/linux-system-roles/template/actions/workflows/woke.yml/badge.svg)](https://github.com/linux-system-roles/template/actions/workflows/woke.yml) - -![template](https://github.com/linux-system-roles/template/workflows/tox/badge.svg) - -A template for an ansible role that configures some GNU/Linux subsystem or -service. A brief description of the role goes here. +Sudo System Role ## Requirements -Any prerequisites that may not be covered by Ansible itself or the role should -be mentioned here. This includes platform dependencies not managed by the -role, hardware requirements, external collections, etc. There should be a -distinction between *control node* requirements (like collections) and -*managed node* requirements (like special hardware, platform provisioning). +This role is only supported on RHEL8.1+/CentOS8.1+ and Fedora distributions. Consider reading fapolicyd documentation before setting it up. ### Collection requirements -For instance, if the role depends on some collections and has a -`meta/collection-requirements.yml` file for installing those dependencies, and -in order to manage `rpm-ostree` systems, it should be mentioned here that the - user should run +None. -```bash -ansible-galaxy collection install -vv -r meta/collection-requirements.yml -``` +## Role Variables -on the *control node* before using the role. +The defaults defined for this role are based on a default RHEL8.4 `/etc/sudoers` configuration. Please check the defaults in [`defaults/main.yml`](defaults/main.yml) prior to running for OS compatibility. -## Role Variables +| Variable Name | Description | Default Value | Variable Type | +| --- | --- | :---: | :---: | +| sudo_rewrite_default_sudoers_file | Use role default or user defined `sudoers_files` definition, replacing your distribution supplied `/etc/sudoers` file. Useful when attempting to deploy new configuration files to the `include_directories` and you do not wish to modify the `/etc/sudoers` file. | True | boolean | +| sudo_remove_unauthorized_included_files | ***Very Dangerous!*** Each existing sudoer file found in the `include_directories` dictionary which have not been defined in `sudoers_files` will be removed. This allows for enforcing a desired state. | False | boolean | +| sudo_visudo_path | Fully-qualified path to the `visudo` binary required for validation of sudoers configuration changes. Added for Operating System compatibility. | /usr/sbin/visudo | string | +| sudo_sudoers_files | Definition of all your sudoers configurations | see [defaults/main.yml](defaults/main.yml)| list of dictionaries | -A description of all input variables (i.e. variables that are defined in -`defaults/main.yml`) for the role should go here as these form an API of the -role. Each variable should have its own section e.g. +## sudo_sudoers_files Dictionary Fields -### template_foo +| Variable Name | Description | Variable Type | +| --- | --- | :---: | +| path | Where to deploy the configuration file to on the filesystem. | string | +| aliases | Optional definition of `cmnd_alias`, `host_alias`, `runas_alias`, or `user_alias` items. | dictionary | +| defaults | This allows you to define the defaults of your sudoers configuration. Default overrides can be perfomed via the [`user_specifications`](#default-override-user_specifications) key.| list | +| include_files | Optional specific files that you would like your configuration to include. This is a list of fully-qualified paths to include via the `#include` option of a sudoers configuration. | list | +| include_directories | Optional specific directories that you would like your configurations to include. This is a list of fully-qualified paths to directories to include via the `#includedir` option of a sudoers configuration. | list | +| user_specifications | List of user specifications and default overrides to apply to a sudoers file configuration. | list | -This variable is required. It is a string that lists the foo of the role. -There is no default value. +### sudo_sudoers_files.aliases Dictionary Fields -### template_bar +| Variable Name | Description | Variable Type | +| --- | --- | :---: | +| cmnd_alias | List of command alias definitions. | list of dictionaries | +| host_alias | List of host alias definitions | list of dictionaries | +| runas_alias | List of runas alias definitions | list of dictionaries | +| user_alias | List of user alias definitions | list of dictionaries | -This variable is optional. It is a boolean that tells the role to disable bar. -The default value is `true`. +#### cmnd_alias Dictionary Fields -Variables that are not intended as input, like variables defined in -`vars/main.yml`, variables that are read from other roles and/or the global -scope (ie. hostvars, group vars, etc.) can be also mentioned here but keep in -mind that as these are probably not part of the role API they may change during -the lifetime. +| Variable Name | Description | Variable Type | +| --- | --- | :---: | +| name | Name of the command alias. | string | +| commands | List of commands to apply to the alias | list | -Example of setting the variables: +#### host_alias Dictionary Fields -```yaml -template_foo: "oof" -template_bar: false -``` +| Variable Name | Description | Variable Type | +| --- | --- | :---: | +| name | Name of the host alias. | string | +| hosts | List of hosts to apply to the alias | list | -## Variables Exported by the Role +#### runas_alias Dictionary Fields -This section is optional. Some roles may export variables for playbooks to -use later. These are analogous to "return values" in Ansible modules. For -example, if a role performs some action that will require a system reboot, but -the user wants to defer the reboot, the role might set a variable like -`template_reboot_needed: true` that the playbook can use to reboot at a more -convenient time. +| Variable Name | Description | Variable Type | +| --- | --- | :---: | +| name | Name of the runas alias | string | +| users | List of users to apply to the alias | list | -Example: +#### user_alias Dictionary Fields -### template_reboot_needed +| Variable Name | Description | Variable Type | +| --- | --- | :---: | +| name | Name of the user_alias | string | +| users | List of users to apply to the alias | list | -Default `false` - if `true`, this means a reboot is needed to apply the changes -made by the role +### user_specifications Dictionary Fields + +This dictionary can be used to assign either user specifications or default overrides. + +#### Standard user_specifications + +| Variable Name | Description | Variable Type | +| --- | --- | :---: | +| users | List of users to apply the specification to. You can use a `user_alias` name as well as user names. | list | +| hosts | List of hosts to apply the specification to. You can use a defined `host_alias` name as well as host names. | list | +| operators | List of operators to apply the specification to. You can use a defined `runas_alias` name as well as user names. | list | +| selinux_role | Optional selinux role to apply to the specification | list | +| selinux_type | Optional selinux type to apply to the specification | list | +| solaris_privs | Optional Solaris privset to apply to the specification | list | +| solaris_limitprivs | Optional Solaris privset to apply to the specification | list | +| tags | Optional list of tags to apply to the specification. | list | +| commands | List of commands to apply the specification to. You can use a defined `cmnd_alias` name as well as commands. | list | + +#### Default Override user_specifications + +| Variable Name | Description | Variable Type | +| --- | --- | :---: | +| defaults | List of defaults to override from the main configuration | list | +| type | Type of default to override, this affects the operator in the configuration ( host -> `@`, user -> `:`, command -> `!`, and runas -> `>`). The type field can be one of the following values: `command`, `host`, `runas`, or `user`. | string | +| commands | Use when `type: command`. List of `cmnd_alias` names as well as commands to override specific default values.| list | +| hosts | Use when `type: host`. List of `host_alias` names as well as individual host names to override specific default values. | list | +| operators | Use when `type: runas`. List of `runas_alias` names as well as individual user names to override specific default values. | list | +| users | Use when `type: user`. List of `user_alias` names as well as individual user names to override specific default values. | list | ## Example Playbook -Including an example of how to use your role (for instance, with variables -passed in as parameters) is always nice for users too: +```yaml +--- +- name: Apply a RHEL Default /etc/sudoers configuration + hosts: all + roles: + - role: linux-system-roles.sudo +``` ```yaml -- name: Manage the template subsystem +--- +- name: Apply custom /etc/sudoers configuration hosts: all vars: - template_foo: "foo foo!" - template_bar: false + sudoers_files: + - path: /etc/sudoers + user_specifications: + - users: + - root + hosts: + - x + commands: + - /usr/bin/ls roles: - - linux-system-roles.template + - role: linux-system-roles.sudo ``` -More examples can be provided in the [`examples/`](examples) directory. These -can be useful, especially for documentation. - -## rpm-ostree +```yaml +--- +- name: Apply a RHEL Default /etc/sudoers configuration + hosts: all + vars: + sudoers_files: + - path: /etc/sudoers + defaults: + - "!visiblepw" + - always_set_home + - match_group_by_gid + - always_query_group_plugin + - env_reset + - secure_path: + - /sbin + - /bin + - /usr/sbin + - /usr/bin + - env_keep: + - COLORS + - DISPLAY + - HOSTNAME + - HISTSIZE + - KDEDIR + - LS_COLORS + - MAIL + - PS1 + - PS2 + - QTDIR + - USERNAME + - LANG + - LC_ADDRESS + - LC_CTYPE + - LC_COLLATE + - LC_IDENTIFICATION + - LC_MEASUREMENT + - LC_MESSAGES + - LC_MONETARY + - LC_NAME + - LC_NUMERIC + - LC_PAPER + - LC_TELEPHONE + - LC_TIME + - LC_ALL + - LANGUAGE + - LINGUAS + - _XKB_CHARSET + - XAUTHORITY + user_specifications: + - users: + - root + hosts: + - ALL + operators: + - ALL + commands: + - ALL + - users: + - "%wheel" + hosts: + - ALL + operators: + - ALL + commands: + - ALL + include_directories: + - /etc/sudoers.d + roles: + - role: linux-system-roles.sudo +``` -See README-ostree.md +```yaml +--- +- name: Apply a multi-file sudoers configuration + hosts: all + tasks: + - name: Configure /etc/sudoers and included files + include_role: + name: linux-system-roles.sudo + vars: + sudoers_rewrite_default_sudoers_file: True + sudoers_remove_unauthorized_included_files: True + sudoers_backup: True + sudoers_backup_path: sudoers-backups + sudoers_files: + - path: /etc/sudoers + defaults: + - "!visiblepw" + - always_set_home + - match_group_by_gid + - always_query_group_plugin + - env_reset + - secure_path: + - /sbin + - /bin + - /usr/sbin + - /usr/bin + - env_keep: + - COLORS + - DISPLAY + - HOSTNAME + - HISTSIZE + - KDEDIR + - LS_COLORS + - MAIL + - PS1 + - PS2 + - QTDIR + - USERNAME + - LANG + - LC_ADDRESS + - LC_CTYPE + - LC_COLLATE + - LC_IDENTIFICATION + - LC_MEASUREMENT + - LC_MESSAGES + - LC_MONETARY + - LC_NAME + - LC_NUMERIC + - LC_PAPER + - LC_TELEPHONE + - LC_TIME + - LC_ALL + - LANGUAGE + - LINGUAS + - _XKB_CHARSET + - XAUTHORITY + user_specifications: + - users: + - root + hosts: + - ALL + operators: + - ALL + commands: + - ALL + - users: + - "%wheel" + hosts: + - ALL + operators: + - ALL + commands: + - ALL + include_directories: + - /etc/sudoers.d + aliases: + cmnd_alias: + - name: PING + commands: + - /bin/ping + user_alias: + - name: PINGERS + users: + - ahuffman + - path: /etc/sudoers.d/pingers + user_specifications: + - type: user + defaults: + - "!requiretty" + users: + - PINGERS + - path: /etc/sudoers.d/root + defaults: + - syslog=auth + user_specifications: + - type: runas + defaults: + - "!set_logname" + operators: + - root +``` ## License -Whenever possible, please prefer MIT. +MIT ## Author Information -An optional section for the role authors to include contact information, or a -website (HTML is not allowed). +Radovan Sroka @rsroka diff --git a/contributing.md b/contributing.md index 44e3ad0..6c64db5 100644 --- a/contributing.md +++ b/contributing.md @@ -1,4 +1,4 @@ -# Contributing to the template Linux System Role +# Contributing to the Sudo Linux System Role ## Where to start @@ -12,10 +12,10 @@ This has all of the common information that all role developers need: * How to create git commits and submit pull requests **Bugs and needed implementations** are listed on -[Github Issues](https://github.com/linux-system-roles/template/issues). +[Github Issues](https://github.com/linux-system-roles/sudo/issues). Issues labeled with -[**help wanted**](https://github.com/linux-system-roles/template/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) +[**help wanted**](https://github.com/linux-system-roles/sudo/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) are likely to be suitable for new contributors! -**Code** is managed on [Github](https://github.com/linux-system-roles/template), using +**Code** is managed on [Github](https://github.com/linux-system-roles/sudo), using [Pull Requests](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests). diff --git a/defaults/main.yml b/defaults/main.yml index 6944529..e8653d6 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -1,8 +1,70 @@ -# SPDX-License-Identifier: MIT --- -# Here is the right place to put the role's input variables. -# This file also serves as a documentation for such a variables. +sudo_rewrite_default_sudoers_file: True +sudo_remove_unauthorized_included_files: False -# Examples of role input variables: -template_foo: foo -template_bar: true +sudo_visudo_path: /usr/sbin/visudo + +sudo_sudoers_files: + - path: /etc/sudoers + defaults: + - "!visiblepw" + - always_set_home + - match_group_by_gid + - always_query_group_plugin + - env_reset + - secure_path: + - /sbin + - /bin + - /usr/sbin + - /usr/bin + - env_keep: + - COLORS + - DISPLAY + - HOSTNAME + - HISTSIZE + - KDEDIR + - LS_COLORS + - MAIL + - PS1 + - PS2 + - QTDIR + - USERNAME + - LANG + - LC_ADDRESS + - LC_CTYPE + - LC_COLLATE + - LC_IDENTIFICATION + - LC_MEASUREMENT + - LC_MESSAGES + - LC_MONETARY + - LC_NAME + - LC_NUMERIC + - LC_PAPER + - LC_TELEPHONE + - LC_TIME + - LC_ALL + - LANGUAGE + - LINGUAS + - _XKB_CHARSET + - XAUTHORITY + user_specifications: + - users: + - root + hosts: + - ALL + operators: + - ALL + commands: + - ALL + - users: + - "%wheel" + hosts: + - ALL + operators: + - ALL + commands: + - ALL + include_directories: + - /etc/sudoers.d + # include_files: [] + # aliases: [] diff --git a/examples/simple.yml b/examples/simple.yml deleted file mode 100644 index 4f1456e..0000000 --- a/examples/simple.yml +++ /dev/null @@ -1,9 +0,0 @@ -# SPDX-License-Identifier: MIT ---- -- name: Example template role invocation - hosts: all - vars: - template_foo: example variable value - template_bar: false - roles: - - linux-system-roles.template diff --git a/examples/simple1.yml b/examples/simple1.yml new file mode 100644 index 0000000..6e1df0a --- /dev/null +++ b/examples/simple1.yml @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: MIT +--- +- name: Apply a RHEL Default /etc/sudoers configuration + hosts: all + roles: + - role: linux-system-roles.sudo diff --git a/examples/simple2.yml b/examples/simple2.yml new file mode 100644 index 0000000..2d6a884 --- /dev/null +++ b/examples/simple2.yml @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: MIT +--- +- name: Apply custom /etc/sudoers configuration + hosts: all + vars: + sudoers_files: + - path: /etc/sudoers + user_specifications: + - users: + - root + hosts: + - x + commands: + - /usr/bin/ls + roles: + - role: linux-system-roles.sudo diff --git a/examples/simple3.yml b/examples/simple3.yml new file mode 100644 index 0000000..fd37061 --- /dev/null +++ b/examples/simple3.yml @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: MIT +--- +- name: Apply a RHEL Default /etc/sudoers configuration + hosts: all + vars: + sudoers_files: + - path: /etc/sudoers + defaults: + - "!visiblepw" + - always_set_home + - match_group_by_gid + - always_query_group_plugin + - env_reset + - secure_path: + - /sbin + - /bin + - /usr/sbin + - /usr/bin + - env_keep: + - COLORS + - DISPLAY + - HOSTNAME + - HISTSIZE + - KDEDIR + - LS_COLORS + - MAIL + - PS1 + - PS2 + - QTDIR + - USERNAME + - LANG + - LC_ADDRESS + - LC_CTYPE + - LC_COLLATE + - LC_IDENTIFICATION + - LC_MEASUREMENT + - LC_MESSAGES + - LC_MONETARY + - LC_NAME + - LC_NUMERIC + - LC_PAPER + - LC_TELEPHONE + - LC_TIME + - LC_ALL + - LANGUAGE + - LINGUAS + - _XKB_CHARSET + - XAUTHORITY + user_specifications: + - users: + - root + hosts: + - ALL + operators: + - ALL + commands: + - ALL + - users: + - "%wheel" + hosts: + - ALL + operators: + - ALL + commands: + - ALL + include_directories: + - /etc/sudoers.d + roles: + - role: linux-system-roles.sudo diff --git a/examples/simple4.yml b/examples/simple4.yml new file mode 100644 index 0000000..bf4c033 --- /dev/null +++ b/examples/simple4.yml @@ -0,0 +1,100 @@ +# SPDX-License-Identifier: MIT +--- +- name: Apply a multi-file sudoers configuration + hosts: all + tasks: + - name: Configure /etc/sudoers and included files + include_role: + name: linux-system-roles.sudo + vars: + sudoers_rewrite_default_sudoers_file: True + sudoers_remove_unauthorized_included_files: True + sudoers_backup: True + sudoers_backup_path: sudoers-backups + sudoers_files: + - path: /etc/sudoers + defaults: + - "!visiblepw" + - always_set_home + - match_group_by_gid + - always_query_group_plugin + - env_reset + - secure_path: + - /sbin + - /bin + - /usr/sbin + - /usr/bin + - env_keep: + - COLORS + - DISPLAY + - HOSTNAME + - HISTSIZE + - KDEDIR + - LS_COLORS + - MAIL + - PS1 + - PS2 + - QTDIR + - USERNAME + - LANG + - LC_ADDRESS + - LC_CTYPE + - LC_COLLATE + - LC_IDENTIFICATION + - LC_MEASUREMENT + - LC_MESSAGES + - LC_MONETARY + - LC_NAME + - LC_NUMERIC + - LC_PAPER + - LC_TELEPHONE + - LC_TIME + - LC_ALL + - LANGUAGE + - LINGUAS + - _XKB_CHARSET + - XAUTHORITY + user_specifications: + - users: + - root + hosts: + - ALL + operators: + - ALL + commands: + - ALL + - users: + - "%wheel" + hosts: + - ALL + operators: + - ALL + commands: + - ALL + include_directories: + - /etc/sudoers.d + aliases: + cmnd_alias: + - name: PING + commands: + - /bin/ping + user_alias: + - name: PINGERS + users: + - ahuffman + - path: /etc/sudoers.d/pingers + user_specifications: + - type: user + defaults: + - "!requiretty" + users: + - PINGERS + - path: /etc/sudoers.d/root + defaults: + - syslog=auth + user_specifications: + - type: runas + defaults: + - "!set_logname" + operators: + - root diff --git a/handlers/main.yml b/handlers/main.yml index 726022e..2820d37 100644 --- a/handlers/main.yml +++ b/handlers/main.yml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: MIT --- -- name: Handler for template to restart services +- name: Handler for role to restart services service: name: "{{ item }}" state: restarted - loop: "{{ __template_services }}" + loop: "{{ __sudo_services }}" diff --git a/meta/main.yml b/meta/main.yml index 0862691..25e4ebf 100644 --- a/meta/main.yml +++ b/meta/main.yml @@ -1,68 +1,21 @@ # SPDX-License-Identifier: MIT --- galaxy_info: - # Replace with role's author name: - author: John Doe - # Replace with the real description of what is role's purpose: - description: Basic template for Linux system roles - # Replace with the company the role's author is member of: - company: John Doe, Inc. - - # If the issue tracker for your role is not on github, uncomment the next - # line and provide a value - # issue_tracker_url: http://example.com/issue/tracker - - # Some suggested licenses: - # - BSD (default) - # - MIT - # - GPLv2 - # - GPLv3 - # - Apache - # - CC-BY + author: Radovan Sroka + description: Sudo system role + company: Red Hat Inc. license: MIT min_ansible_version: "2.9" - - # Optionally specify the branch Galaxy will use when accessing the GitHub - # repo for this role. During role install, if no tags are available, Galaxy - # will use this branch. During import Galaxy will access files on this - # branch. If Travis integration is configured, only notifications for this - # branch will be accepted. Otherwise, in all cases, the repo's default branch - # (usually main) will be used. - # github_branch: - - # - # platforms is a list of platforms, and each platform has a name and a list - # of versions. - # - # platforms: - # - name: Fedora - # versions: - # - all - # - "25" - # - name: SomePlatform - # versions: - # - all - # - "1.0" - # - "7" - # - "99.99" platforms: - # Replace the below with your platform list: - name: Fedora versions: - all - name: EL versions: - - all + - "8" + - "9" galaxy_tags: [] - # List tags for your role here, one per line. A tag is a keyword that - # describes and categorizes the role. Users find roles by searching for tags. - # Be sure to remove the '[]' above, if you add tags to this list. - # - # NOTE: A tag is limited to a single word comprised of alphanumeric - # characters. Maximum 20 tags per role. dependencies: [] -# List your role dependencies here, one per line. Be sure to remove the '[]' -# above, if you add dependencies to this list. diff --git a/tasks/main.yml b/tasks/main.yml index f510ef4..b799de8 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -1,27 +1,83 @@ -# SPDX-License-Identifier: MIT --- -- name: Set platform/version specific variables - include_tasks: tasks/set_vars.yml - -# Examples of some tasks: -- name: Ensure required packages are installed +- name: Ensure sudo is installed package: - name: "{{ __template_packages }}" + name: sudo state: present - use: "{{ (__template_is_ostree | d(false)) | - ternary('ansible.posix.rhel_rpm_ostree', omit) }}" + register: sudo_installed + +- name: Set include directories variable + set_fact: + sudo_include_dirs: "{{ sudo_sudoers_files + | selectattr('include_directories', 'defined') + | map(attribute='include_directories') | flatten }}" + +- name: Set sudoer file paths variable + set_fact: + sudo_file_paths: "{{ sudo_sudoers_files + | selectattr('path', 'defined') + | map(attribute='path') | flatten }}" + +- name: Ensure include directories are created + file: + path: {{ item }} + owner: root + group: root + mode: 0750 + state: directory + with_items: {{ sudo_include_dirs }} + when: sudo_include_dirs | length > 0 -- name: Ensure required services are enabled and started - service: - name: "{{ item }}" - state: started - enabled: true - loop: "{{ __template_services }}" +- name: Ensure sudoers include files are configured + template: + src: sudoers.j2 + dest: {{ item.path }} + owner: root + group: root + mode: 0440 + validate: '{{ sudo_visudo_path }} -cf %s' + with_items: {{ sudo_sudoers_files }} +# become: True + loop_control: + label: {{ item.path }} + when: item.path != '/etc/sudoers' -- name: Generate /etc/{{ __template_foo_config }} +- name: Ensure /etc/sudoers is configured template: - src: "{{ __template_foo_config }}.j2" - dest: /etc/{{ __template_foo_config }} - backup: true - mode: 0400 - notify: Handler for template to restart services + src: sudoers.j2 + dest: /etc/sudoers + owner: root + group: root + mode: 0440 + validate: '{{ sudo_visudo_path }} -cf %s' + with_items: {{ sudo_sudoers_files }} + loop_control: + label: {{ item.path }} + when: + - item.path == '/etc/sudoers' + - sudo_rewrite_default_sudoers_file | bool + +- name: Remove unauthorized included sudoer files + when: + - sudo_remove_unauthorized_included_files | bool + - sudo_include_dirs | length > 0 + block: + - name: Search for sudoers files in included directories + find: + paths: {{ sudo_include_dirs }} + recurse: True + hidden: True + file_type: any + register: sudo_existing_included_files + + - name: Set unauthorized included sudoers files variable + set_fact: + sudo_unauthorized_files: "{{ sudo_existing_included_files['files'] + | selectattr('path', 'defined') | map(attribute='path') + | flatten | difference(sudo_file_paths) }}" + + - name: Remove unauthorized included sudoers files + file: + path: {{ item }} + state: absent + with_items: {{ sudo_unauthorized_files }} + when: sudo_unauthorized_files | length > 0 diff --git a/templates/foo.conf.j2 b/templates/foo.conf.j2 deleted file mode 100644 index 5fc204b..0000000 --- a/templates/foo.conf.j2 +++ /dev/null @@ -1,9 +0,0 @@ -# SPDX-License-Identifier: MIT -# -# Example of a template of configuration file -# -{{ ansible_managed | comment }} -{{ "system_role:template" | comment(prefix="", postfix="") }} -[foo] -foo = {{ template_foo }} -bar = {{ template_bar }} diff --git a/templates/sudoers.j2 b/templates/sudoers.j2 new file mode 100644 index 0000000..35b8e74 --- /dev/null +++ b/templates/sudoers.j2 @@ -0,0 +1,111 @@ +{{ ansible_managed | default('Ansible managed') | comment }} +{{ "system_role:sudo" | comment(prefix="", postfix="") }} +{% if item.defaults is defined %} + +# Default specifications +{% for default in item.defaults %} +{% if default is mapping %} +{% for name, values in default.items() %} +{% if name == 'secure_path' %} +Defaults {{ name }} = {% for item in values %}{% if not loop.last %}{{ item }}:{% else %}{{ item }}{% endif %}{% endfor %} + +{% else %} +{% for items in values | list | slice(6) %} +{% if items %} +Defaults {{ name }} {% if not loop.first %}+{% endif %}= "{{ items | list | join(' ') }}" +{% endif -%} +{% endfor %} +{% endif %} +{% endfor %} +{% elif default | first == ':' %} +Defaults{{ default }} +{% else %} +Defaults {{ default }} +{% endif %} +{% endfor %} + +{% endif %} +{% if item.aliases is defined %} +# Alias specifications +{% if item.aliases.cmnd_alias is defined %} +## Command Aliases +{% for ca in item.aliases.cmnd_alias %} +Cmnd_Alias {{ ca.name }} = {% for cmnd in ca.commands %}{% if not loop.last %}{{ cmnd }}, {% else %}{{ cmnd }}{% endif %}{% endfor %} + +{% endfor %} + +{% endif %} +{% if item.aliases.host_alias is defined %} +## Host Aliases +{% for ha in item.aliases.host_alias %} +Host_Alias {{ ha.name }} = {% for host in ha.hosts %}{% if not loop.last %}{{ host }}, {% else %}{{ host }}{% endif %}{% endfor %} + +{% endfor %} + +{% endif %} +{% if item.aliases.runas_alias is defined %} +## Runas Aliases +{% for ra in item.aliases.runas_alias %} +Runas_Alias {{ ra.name }} = {% for user in ra.users %}{% if not loop.last %}{{ user }}, {% else %}{{ user }}{% endif %}{% endfor %} + +{% endfor %} + +{% endif %} +{% if item.aliases.user_alias is defined %} +## User Aliases +{% for ua in item.aliases.user_alias %} +User_Alias {{ ua.name }} = {% for user in ua.users %}{% if not loop.last %}{{ user }}, {% else %}{{ user }}{% endif %}{% endfor %} + +{% endfor %} + +{% endif %} +{% endif %} +{% if item.user_specifications is defined %} +{% if item.user_specifications | selectattr('type', 'undefined') | flatten | length > 0 %} +# User specifications +{% for spec in item.user_specifications %} +{% if spec.type is undefined %} +{% for user in spec.users %}{% if not loop.last %}{{ user }}, {% else %}{{ user }}{% endif %}{% endfor %} {% for host in spec.hosts %}{% if not loop.last %}{{ host }}, {% else %}{{ host }}{% endif %}{% endfor %}={% if spec.operators is defined %}({% for op in spec.operators %}{% if not loop.last%}{{ op }}, {% else %}{{ op }}{% endif %}{% endfor %}){% endif %} {% if spec.selinux_type is defined %}TYPE={% for type in spec.selinux_type %}{% if not loop.last %}{{ type }}, {% else %}{{ type }} {% endif %}{% endfor %}{% endif %}{% if spec.selinux_role is defined %}ROLE={% for role in spec.selinux_role %}{% if not loop.last %}{{ role }}, {% else %}{{ role }} {% endif %}{% endfor %}{% endif %}{% if spec.solaris_privs is defined %}PRIVS={% for priv in spec.solaris_privs %}{% if not loop.last %}{{ priv }}, {% else %}{{ priv }} {% endif %}{% endfor %}{% endif %}{% if spec.solaris_limitprivs is defined %}LIMITPRIVS={% for lpriv in spec.solaris_limitprivs %}{% if not loop.last %}{{ lpriv }}, {% else %}{{ lpriv }} {% endif %}{% endfor %}{% endif %}{% if spec.tags is defined %}{% for tag in spec.tags %}{{ tag }}:{% endfor %} {% endif %}{% for cmnd in spec.commands %}{% if not loop.last %}{{ cmnd }}, {% else %}{{ cmnd }}{% endif %}{% endfor %} + +{% endif %} +{% endfor %} +{% endif %} + +{% endif %} +{% if item.user_specifications | selectattr('defaults', 'defined') | map(attribute='defaults') | flatten | length > 0 %} +# Default override specifications +{% for spec in item.user_specifications %} +{% if spec.type is defined %} +{% if spec.type == 'user'%} +Defaults:{% for user in spec.users %}{% if not loop.last %}{{ user }}, {% else %}{{ user }} {% endif %}{% endfor %}{% for default in spec.defaults %}{% if not loop.last %}{{ default }}, {% else %}{{ default }}{% endif %}{% endfor %} + +{% elif spec.type == 'runas' %} +Defaults>{% for op in spec.operators %}{% if not loop.last %}{{ op }}, {% else %}{{ op }} {% endif %}{% endfor %}{% for default in spec.defaults %}{% if not loop.last %}{{ default }}, {% else %}{{ default }}{% endif %}{% endfor %} + +{% elif spec.type == 'host' %} +Defaults@{% for host in spec.hosts %}{% if not loop.last %}{{ host }}, {% else %}{{ host }} {% endif %}{% endfor %}{% for default in spec.defaults %}{% if not loop.last %}{{ default }}, {% else %}{{ default }}{% endif %}{% endfor %} + +{% elif spec.type == 'command' %} +Defaults!{% for cmnd in spec.commands %}{% if not loop.last %}{{ cmnd }}, {% else %}{{ cmnd }} {% endif %}{% endfor %}{% for default in spec.defaults %}{% if not loop.last %}{{ default }}, {% else %}{{ default }}{% endif %}{% endfor %} + +{% endif %} +{% endif %} +{% endfor %} + +{% endif %} +{% if item.include_files is defined or item.include_directories is defined %} +# Includes +{% if item.include_files is defined and item.include_files | length > 0 %} +## Include files +{% for file in item.include_files %} +#include {{ file }} +{% endfor %} + +{% endif %} +{% if item.include_directories is defined and item.include_directories | length > 0 %} +## Include directories +{% for dir in item.include_directories %} +#includedir {{ dir }} +{% endfor %} +{% endif %} +{% endif %} diff --git a/tests/roles/linux-system-roles.sudo/defaults b/tests/roles/linux-system-roles.sudo/defaults new file mode 120000 index 0000000..30459c3 --- /dev/null +++ b/tests/roles/linux-system-roles.sudo/defaults @@ -0,0 +1 @@ +../../../defaults \ No newline at end of file diff --git a/tests/roles/linux-system-roles.sudo/handlers b/tests/roles/linux-system-roles.sudo/handlers new file mode 120000 index 0000000..3b11237 --- /dev/null +++ b/tests/roles/linux-system-roles.sudo/handlers @@ -0,0 +1 @@ +../../../handlers \ No newline at end of file diff --git a/tests/roles/linux-system-roles.sudo/meta b/tests/roles/linux-system-roles.sudo/meta new file mode 120000 index 0000000..8df72eb --- /dev/null +++ b/tests/roles/linux-system-roles.sudo/meta @@ -0,0 +1 @@ +../../../meta \ No newline at end of file diff --git a/tests/roles/linux-system-roles.sudo/tasks b/tests/roles/linux-system-roles.sudo/tasks new file mode 120000 index 0000000..d97297b --- /dev/null +++ b/tests/roles/linux-system-roles.sudo/tasks @@ -0,0 +1 @@ +../../../tasks \ No newline at end of file diff --git a/tests/roles/linux-system-roles.sudo/templates b/tests/roles/linux-system-roles.sudo/templates new file mode 120000 index 0000000..0e4c94f --- /dev/null +++ b/tests/roles/linux-system-roles.sudo/templates @@ -0,0 +1 @@ +../../../templates \ No newline at end of file diff --git a/tests/roles/linux-system-roles.sudo/vars b/tests/roles/linux-system-roles.sudo/vars new file mode 120000 index 0000000..663079d --- /dev/null +++ b/tests/roles/linux-system-roles.sudo/vars @@ -0,0 +1 @@ +../../../vars \ No newline at end of file diff --git a/tests/setup-snapshot.yml b/tests/setup-snapshot.yml index a7704df..1083e1d 100644 --- a/tests/setup-snapshot.yml +++ b/tests/setup-snapshot.yml @@ -4,7 +4,7 @@ tasks: - name: Set platform/version specific variables include_role: - name: linux-system-roles.template + name: linux-system-roles.sudo tasks_from: set_vars.yml public: true diff --git a/tests/tests_default.yml b/tests/tests_default.yml index 4457422..1a72731 100644 --- a/tests/tests_default.yml +++ b/tests/tests_default.yml @@ -4,10 +4,10 @@ hosts: all gather_facts: false # test that role works in this case roles: - - linux-system-roles.template + - linux-system-roles.sudo tasks: - name: Check header for ansible_managed, fingerprint include_tasks: tasks/check_header.yml vars: - __file: /etc/foo.conf - __fingerprint: system_role:template + __file: /etc/sudoers + __fingerprint: system_role:sudo diff --git a/tests/tests_include_vars_from_parent.yml b/tests/tests_include_vars_from_parent.yml index fc795dd..749b0ef 100644 --- a/tests/tests_include_vars_from_parent.yml +++ b/tests/tests_include_vars_from_parent.yml @@ -44,7 +44,7 @@ import_role: name: caller vars: - roletoinclude: linux-system-roles.template + roletoinclude: linux-system-roles.sudo - name: Cleanup file: diff --git a/tests/tests_role_applied.ok b/tests/tests_role_applied.ok new file mode 100644 index 0000000..3e29e25 --- /dev/null +++ b/tests/tests_role_applied.ok @@ -0,0 +1,25 @@ +# +# Ansible managed +# + +# Default specifications +Defaults !visiblepw +Defaults always_set_home +Defaults match_group_by_gid +Defaults always_query_group_plugin +Defaults env_reset +Defaults secure_path = /sbin:/bin:/usr/sbin:/usr/bin +Defaults env_keep = "COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR" +Defaults env_keep += "LS_COLORS MAIL PS1 PS2 QTDIR" +Defaults env_keep += "USERNAME LANG LC_ADDRESS LC_CTYPE LC_COLLATE" +Defaults env_keep += "LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME" +Defaults env_keep += "LC_NUMERIC LC_PAPER LC_TELEPHONE LC_TIME LC_ALL" +Defaults env_keep += "LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY" + +# User specifications +root ALL=(ALL) ALL +%wheel ALL=(ALL) ALL + +# Includes +## Include directories +#includedir /etc/sudoers.d diff --git a/tests/tests_role_applied.yml b/tests/tests_role_applied.yml new file mode 100644 index 0000000..40ce662 --- /dev/null +++ b/tests/tests_role_applied.yml @@ -0,0 +1,63 @@ +--- +- name: Basic test for Sudo + hosts: all + tasks: + - name: Run tests + block: + - name: Run the role + include_role: + name: linux-system-roles.sudo + vars: + sudo_rewrite_default_sudoers_file: True + sudo_remove_unauthorized_included_files: True + + - name: Create temp test directory + tempfile: + path: /var/tmp + prefix: sudo_ + state: directory + register: __sudo_tmpdir + + - name: Backup sudoers + copy: + src: '/etc/sudoers' + dest: "{{ __sudo_tmpdir }}/sudoers" + owner: root + group: root + mode: "0644" + remote_src: true + + - name: Backup sudoers.d + copy: + src: '/etc/sudoers.d' + dest: "{{ __sudo_tmpdir }}/sudoers.d" + owner: root + group: root + mode: "0644" + remote_src: true + + - name: Check sudoers + command: >- + diff -u /etc/sudoers ./tests_role_applied.ok + - name: Restore sudoers + copy: + src: "{{ __sudo_tmpdir }}/sudoers" + dest: '/etc/sudoers' + owner: root + group: root + mode: "0644" + remote_src: true + + - name: Restore sudoers.d + copy: + src: "{{ __sudo_tmpdir }}/sudoers.d" + dest: '/etc/sudoers.d' + owner: root + group: root + mode: "0644" + remote_src: true + + - name: Clean up temp directory + file: + path: "{{ __sudo_tmpdir }}" + state: absent diff --git a/vars/main.yml b/vars/main.yml index 24f178c..64437c7 100644 --- a/vars/main.yml +++ b/vars/main.yml @@ -5,11 +5,11 @@ # value in a platform/version specific file in vars/ # Examples of non-distribution specific (generic) internal variables: -__template_foo_config: foo.conf -__template_packages: [] -__template_services: [] +__sudo_sudoers_config: /etc/sudoers +__sudo_packages: sudo +__sudo_services: [] # ansible_facts required by the role -__template_required_facts: +__sudo_required_facts: - distribution - distribution_major_version - distribution_version @@ -17,5 +17,5 @@ __template_required_facts: # the subsets of ansible_facts that need to be gathered in case any of the # facts in required_facts is missing; see the documentation of # the 'gather_subset' parameter of the 'setup' module -__template_required_facts_subsets: "{{ ['!all', '!min'] + - __template_required_facts }}" +__sudo_required_facts_subsets: "{{ ['!all', '!min'] + + __sudo_required_facts }}"